How do I get an indeterminate number of command line arguments in a bash script?

55 Views Asked by At

I am writing a bash script to be run on macOS Sonoma

$bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin23)
Copyright (C) 2007 Free Software Foundation, Inc.

My script is to read a source file line by line, apply some logic to each line, and write it into one of the target files.

#!/bin/bash

if [[ $# -eq 0 ]]; then
    echo "Usage: $0 <source file> <target 1> <target 2> ... <target n>"
    exit 1
elif [[ $# -eq 1 ]]; then
    echo "Usage: $0 <source file> <target 1> <target 2> ... <target n>"
    exit 1
fi

numFiles=$(($# - 1))
echo "${numFiles} target files"

sourceFile="$1"
echo "Source file: ${sourceFile}"

targetFiles=(${@: 2})
echo "Target files: ${targetFiles}"

I would expect targetFiles to be an array of the target files.

However, when I run this as

splitFile.sh source.txt t1.txt t2.txt t3.txt

I only get

3 target files
Source file: source.txt
Target files: t1.txt

I would expect the last line to be

Target files: t1.txt t2.txt t3.txt

How do I fix this?

3

There are 3 best solutions below

4
Vihung On BEST ANSWER

targetFiles is an array. To print the array, you need to use array notation

echo "Target files: ${targetFiles[*]}"

will give you all values.

1
Gilles Quénot On

You need:

targetFiles=($@)
echo "Target files: ${targetFiles[@]:1}"

The variable:

${targetFiles}

is wrong. For an array, always use ${array[<indice(s)>]}


Always pass your scripts to https://shellcheck.net before asking help here.

You could discover:

echo "Target files: ${targetFiles}"
                    ^------------^ SC2128 (warning): Expanding an array without an index only gives the first element.
2
chepner On

There's really no need for an array. You can (and arguably should, given how old bash 3.2 is) write a POSIX-compliant script that doesn't require any bash extensions here.

#!/bin/sh

if [ $# -eq 0 ]; then
    echo "Usage: $0 <source file> <target 1> <target 2> ... <target n>"
    exit 1
elif [ $# -eq 1 ]; then
    echo "Usage: $0 <source file> <target 1> <target 2> ... <target n>"
    exit 1
fi


sourceFile="$1"
echo "Source file: ${sourceFile}"

shift

numFiles=$(($#))
echo "${numFiles} target files"


echo "Target files: $*"

for target in "$@"; do
   ...
done