Bash 스크립트에서 인수를 반복하는 방법


899

쉘 / bash 스크립트를 만들고 싶은 복잡한 명령이 있습니다. 나는 $1쉽게 다음 과 같이 쓸 수 있습니다.

foo $1 args -o $1.ext

여러 입력 이름을 스크립트에 전달할 수 있기를 원합니다. 올바른 방법은 무엇입니까?

물론 공백이있는 파일 이름을 처리하고 싶습니다.


67
참고로 항상 매개 변수를 따옴표 안에 넣는 것이 좋습니다. parm에 언제 공백이 포함되어 있는지 알 수 없습니다.
David R Tribble

답변:


1480

"$@"모든 인수를 나타내는 데 사용하십시오 .

for var in "$@"
do
    echo "$var"
done

각 인수를 반복하여 별도의 줄에 인쇄합니다. $ @는 인용 부호 안에 공백이있는 경우 인수가 올바르게 분류된다는 점을 제외하고는 $ *처럼 동작합니다.

sh test.sh 1 2 '3 4'
1
2
3 4

38
덧붙여서, 다른 장점 중 하나는 "$@"위치 매개 변수가 없으면 아무것도 확장하지 않고 "$*"빈 문자열로 확장하면 인수가없는 것과 빈 인수가 다른 것입니다. ${1:+"$@"} in / bin / sh`를 참조하십시오 .
Jonathan Leffler

9
이 표기법은 함수에 대한 모든 인수에 액세스하기 위해 쉘 함수에서도 사용해야합니다.
Jonathan Leffler

큰 따옴표를 삭제하여 $var인수를 실행할 수있었습니다.
eonist

두 번째 논쟁에서 어떻게 시작합니까? 나는 이것이 필요하지만 항상 두 번째 인수에서 시작한다는 것을 의미합니다. $ 1을 건너 뜁니다.
m4l490n

4
@ m4l490n :을 shift버리고 $1모든 후속 요소를 아래로 이동합니다.
MSalters

239

VonC가 삭제 한 답변 을 다시 작성하십시오 .

Robert Gamble 의 간결한 답변은 질문을 직접 처리합니다. 이것은 공백을 포함하는 파일 이름의 일부 문제를 증폭시킵니다.

참조 : $ {1 + "$를 @"} / 빈 / 쉬에

기본 논문 : "$@" 정확하고 $*(인용되지 않은) 거의 항상 잘못되었습니다. 이 때문입니다 "$@"작품의 벌금 인수는 공백을 포함 할 때와 동일하게 작동 $*그렇지 않은 경우입니다. 어떤 상황에서는 "$*"괜찮습니다. 그러나 "$@"대개는 아니지만 항상 같은 장소에서 작동합니다. 인용 부호, $@$*상응하는 (그리고 거의 항상 잘못)입니다.

그래서, 차이 무엇인가 $*, $@, "$*", 그리고 "$@"? 그것들은 모두 '쉘에 대한 모든 주장'과 관련이 있지만 다른 일을합니다. 인용 부호로 둘러싸이지 않은, 때 $*$@같은 일을한다. 각 '단어'(공백이 아닌 순서)를 별도의 인수로 취급합니다. 인용 된 형식은 상당히 다릅니다 "$*". 인수 목록을 공백으로 구분 된 단일 문자열로 "$@"취급하지만 인수는 명령 줄에 지정했을 때와 거의 동일하게 취급합니다. "$@"위치 인수가 없으면 전혀 확장되지 않습니다. "$*"빈 문자열로 확장합니다. 예, 차이가 있지만 인식하기가 어렵습니다. (비표준) 명령을 도입 한 후 아래에서 자세한 정보를 참조하십시오 al.

2 차 논문 : 공백이있는 인수를 처리 한 다음 다른 명령으로 전달해야하는 경우 비표준 도구가 필요할 수 있습니다. (또는 배열을주의해서 사용해야합니다 .와 "${array[@]}"유사 하게 동작합니다 "$@".)

예:

    $ mkdir "my dir" anotherdir
    $ ls
    anotherdir      my dir
    $ cp /dev/null "my dir/my file"
    $ cp /dev/null "anotherdir/myfile"
    $ ls -Fltr
    total 0
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 my dir/
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 anotherdir/
    $ ls -Fltr *
    my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ ls -Fltr "./my dir" "./anotherdir"
    ./my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    ./anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ var='"./my dir" "./anotherdir"' && echo $var
    "./my dir" "./anotherdir"
    $ ls -Fltr $var
    ls: "./anotherdir": No such file or directory
    ls: "./my: No such file or directory
    ls: dir": No such file or directory
    $

왜 작동하지 않습니까? 쉘이 변수를 확장하기 전에 따옴표를 처리하기 때문에 작동하지 않습니다. 따라서 쉘에 포함 된 따옴표에주의를 기울이려면 다음 $var을 사용해야합니다 eval.

    $ eval ls -Fltr $var
    ./my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    ./anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ 

" He said, "Don't do this!""(따옴표와 큰 따옴표 및 공백 포함)와 같은 파일 이름이있는 경우 실제로 까다로워집니다 .

    $ cp /dev/null "He said, \"Don't do this!\""
    $ ls
    He said, "Don't do this!"       anotherdir                      my dir
    $ ls -l
    total 0
    -rw-r--r--   1 jleffler  staff    0 Nov  1 15:54 He said, "Don't do this!"
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 anotherdir
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 my dir
    $ 

쉘 (모두)은 그런 것들을 다루기가 쉽지 않기 때문에 많은 유닉스 프로그램은 잘 다루지 못한다. Unix에서 파일 이름 (단일 구성 요소)은 슬래시 및 NUL을 제외한 모든 문자를 포함 할 수 있습니다 '\0'. 그러나 쉘은 경로 이름의 어느 곳에도 공백이나 줄 바꿈 또는 탭을 권장하지 않습니다. 표준 유닉스 파일 이름에 공백 등이 포함되어 있지 않은 이유도 있습니다.

공백과 다른 까다로운 문자가 포함될 수있는 파일 이름을 다룰 때는 매우주의해야하며, 오래 전에 유닉스에서 표준이 아닌 프로그램이 필요하다는 것을 알았습니다. 나는 그것을 호출합니다 escape(버전 1.1은 1989-08-23T16 : 01 : 45Z입니다).

다음은 escapeSCCS 제어 시스템과 함께 사용중인 예입니다 . delta(think check-in ) 및 get(think check-out )을 모두 수행하는 커버 스크립트입니다 . 다양한 인수, 특히 -y(변경 한 이유)에는 공백과 줄 바꿈이 포함됩니다. 스크립트는 1992 년부터 사용되므로 $(cmd ...)표기법 대신 백틱을 사용 #!/bin/sh하고 첫 번째 줄 에는 사용하지 않습니다 .

:   "@(#)$Id: delget.sh,v 1.8 1992/12/29 10:46:21 jl Exp $"
#
#   Delta and get files
#   Uses escape to allow for all weird combinations of quotes in arguments

case `basename $0 .sh` in
deledit)    eflag="-e";;
esac

sflag="-s"
for arg in "$@"
do
    case "$arg" in
    -r*)    gargs="$gargs `escape \"$arg\"`"
            dargs="$dargs `escape \"$arg\"`"
            ;;
    -e)     gargs="$gargs `escape \"$arg\"`"
            sflag=""
            eflag=""
            ;;
    -*)     dargs="$dargs `escape \"$arg\"`"
            ;;
    *)      gargs="$gargs `escape \"$arg\"`"
            dargs="$dargs `escape \"$arg\"`"
            ;;
    esac
done

eval delta "$dargs" && eval get $eflag $sflag "$gargs"

(요즘에는 escape를 너무 철저하게 사용하지 않을 것입니다. -e예를 들어 인수 와 함께 필요 하지는 않지만 전반적으로 이것은을 사용하는 간단한 스크립트 중 하나입니다 escape.)

escape프로그램은 단순히 오히려 같은 인수를 출력 echo 하지 않습니다하지만 인수가 사용하기 위해 보호되도록 eval(한 단계 eval; I 원격 쉘 실행했던 프로그램이 어떻게, 그리고 출력을 탈출해야한다는 escape).

    $ escape $var
    '"./my' 'dir"' '"./anotherdir"'
    $ escape "$var"
    '"./my dir" "./anotherdir"'
    $ escape x y z
    x y z
    $ 

나는 al한 줄에 하나씩 인수를 나열하는 또 다른 프로그램을 가지고 있습니다 (그리고 1987-01-27T14 : 35 : 49의 버전 1.1). 스크립트를 디버깅 할 때 명령 줄에 연결하여 실제로 어떤 인수가 명령에 전달되는지 확인할 수 있으므로 가장 유용합니다.

    $ echo "$var"
    "./my dir" "./anotherdir"
    $ al $var
    "./my
    dir"
    "./anotherdir"
    $ al "$var"
    "./my dir" "./anotherdir"
    $

[ 추가 : 이제 다양한 "$@"표기법 의 차이점을 보여주기 위해 여기에 또 하나의 예가 있습니다.

$ cat xx.sh
set -x
al $@
al $*
al "$*"
al "$@"
$ sh xx.sh     *      */*
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al 'He said, "Don'\''t do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file'
He said, "Don't do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file
+ al 'He said, "Don'\''t do this!"' anotherdir 'my dir' xx.sh anotherdir/myfile 'my dir/my file'
He said, "Don't do this!"
anotherdir
my dir
xx.sh
anotherdir/myfile
my dir/my file
$

명령 줄 **/*명령 줄 사이에 원래 공백이 유지되지 않습니다 . 또한 다음을 사용하여 셸에서 '명령 줄 인수'를 변경할 수 있습니다.

set -- -new -opt and "arg with space"

' -new', ' -opt', ' and'및 ' arg with space'의 4 가지 옵션이 설정 됩니다.
]

흠, 그것은 꽤 긴 대답입니다 . 아마도 주석 이 더 나은 용어 일 것입니다. escape요청시 제공되는 소스 코드 (gmail dot com에서 이름으로 성으로 이메일 전송). 의 소스 코드 al는 매우 간단합니다.

#include <stdio.h>
int main(int argc, char **argv)
{
    while (*++argv != 0)
        puts(*argv);
    return(0);
}

그게 다야. test.shRobert Gamble이 보여준 스크립트 와 동일 하며 쉘 함수로 작성할 수 있습니다 (그러나 쉘 함수는 처음 썼을 때 Bourne 쉘의 로컬 버전에는 존재하지 않았습니다 al).

또한 al간단한 쉘 스크립트로 작성할 수 있습니다 .

[ $# != 0 ] && printf "%s\n" "$@"

인수가 전달되지 않은 경우 출력을 생성하지 않도록 조건이 필요합니다. 이 printf명령은 형식 문자열 인수 만있는 빈 줄을 생성하지만 C 프로그램은 아무 것도 생성하지 않습니다.


132

Robert의 대답은 정확 sh하며 잘 작동 합니다. 훨씬 더 단순화 할 수 있습니다.

for i in "$@"

다음과 같습니다.

for i

즉, 당신은 아무것도 필요하지 않습니다!

테스트 ( $명령 프롬프트 임) :

$ set a b "spaces here" d
$ for i; do echo "$i"; done
a
b
spaces here
d
$ for i in "$@"; do echo "$i"; done
a
b
spaces here
d

나는 Kernighan과 Pike의 Unix Programming Environment 에서 이것에 대해 처음 읽었습니다 .

에서 다음 bashhelp for문서화하십시오.

for NAME [in WORDS ... ;] do COMMANDS; done

경우 'in WORDS ...;'존재하지 않는, 다음 'in "$@"'가정한다.


4
동의하지 않습니다. 암호의 "$@"의미가 무엇인지 알아야 for i하며, 의미 가 무엇인지 알면 쉽게 읽을 수 for i in "$@"있습니다.
Alok Singhal

10
나는 for i그것이 키 입력을 저장하기 때문에 더 낫다고 주장하지 않았다 . 나는의 가독성을 비교 for i하고 for i in "$@".
Alok Singhal

이것이 내가 찾던 것입니다-명시 적으로 참조 할 필요가없는 루프에서 $ @의 가정을 무엇이라고 부릅니까? $ @ 참조를 사용하지 않으면 단점이 있습니까?
qodeninja

58

간단한 경우에는을 사용할 수도 있습니다 shift. 인수 목록을 대기열처럼 취급합니다. 각각 shift은 첫 번째 인수를 버리고 나머지 인수 각각의 인덱스는 감소합니다.

#this prints all arguments
while test $# -gt 0
do
    echo "$1"
    shift
done

shiftfor 루프에서 수행하면 해당 요소는 여전히 배열의 일부이지만 shift예상대로 작동 하기 때문에이 대답이 더 좋습니다 .
DavidG

2
echo "$1"인수 문자열의 값에서 간격을 유지하기 위해 사용해야 합니다. 또한 (사소한 문제) 이것은 파괴적입니다. 인수를 모두 이동시킨 경우에는 인수를 재사용 할 수 없습니다. 종종 문제가되지 않습니다. 어쨌든 인수 목록을 한 번만 처리하면됩니다.
Jonathan Leffler

1
"-o outputfile"과 같은 플래그 인수를 처리하기 위해 스위치 케이스에서 두 개의 인수를 쉽게 가져갈 수 있기 때문에 시프트가 더 낫습니다.
Baxissimo

반면에, 플래그 인수를 구문 분석하기 시작하면 bash보다 더 강력한 스크립트 언어를 고려할 수 있습니다. :)
nuoritoveri

4
이 솔루션은 몇 가지 매개 변수 값이 필요한 경우 더 좋습니다. 예를 들어 --file myfile.txt $ 1은 매개 변수이고 $ 2는 값이며 다른 인수를 건너 뛰어야 할 경우 shift를 두 번 호출합니다.
Daniele Licitra

16

예를 들어 모든 요소를 ​​반복하지 않으려는 경우 배열 요소로 액세스 할 수도 있습니다

argc=$#
argv=("$@")

for (( j=0; j<argc; j++ )); do
    echo "${argv[j]}"
done

5
나는 argv = ($ @) 줄이 argv = ( "$ @")이어야한다고 생각한다. 공백이있는 다른 현명한 인수는 올바르게 처리되지 않는다
kdubs

올바른 구문이 argv = ( "$ @")인지 확인합니다. 인용 된 매개 변수가있는 경우 마지막 값을 얻지 않는 한. 예를 들어, 다음과 같은 것을 사용하면 ./my-script.sh param1 "param2 is a quoted param":-argv = ($ @)를 사용하면 [param1, param2]가 표시됩니다.-argv = ( "$ @")를 사용하면 [param1, param2는 인용 된 매개 변수입니다.]
jseguillon

2
aparse() {
while [[ $# > 0 ]] ; do
  case "$1" in
    --arg1)
      varg1=${2}
      shift
      ;;
    --arg2)
      varg2=true
      ;;
  esac
  shift
done
}

aparse "$@"

1
아래에 명시된 바와 같이 [ $# != 0 ]더 좋습니다. 상기에서, #$해야 $#처럼while [[ $# > 0 ]] ...
hute37

1

baz의 답변을 증폭하여 인덱스로 인수 목록을 열거 해야하는 경우 (예 : 특정 단어 검색) 목록을 복사하거나 변경하지 않고도이 작업을 수행 할 수 있습니다.

인수 목록을 이중 대시 ( "-")로 분할하고 대시 앞의 인수를 한 명령에 전달하고 대시 뒤의 인수를 다른 명령에 전달한다고 가정하십시오.

 toolwrapper() {
   for i in $(seq 1 $#); do
     [[ "${!i}" == "--" ]] && break
   done || return $? # returns error status if we don't "break"

   echo "dashes at $i"
   echo "Before dashes: ${@:1:i-1}"
   echo "After dashes: ${@:i+1:$#}"
 }

결과는 다음과 같아야합니다.

 $ toolwrapper args for first tool -- and these are for the second
 dashes at 5
 Before dashes: args for first tool
 After dashes: and these are for the second

1

getopt 스크립트에서 명령을 사용하여 명령 행 옵션 또는 매개 변수를 형식화하십시오.

#!/bin/bash
# Extract command line options & values with getopt
#
set -- $(getopt -q ab:cd "$@")
#
echo
while [ -n "$1" ]
do
case "$1" in
-a) echo "Found the -a option" ;;
-b) param="$2"
echo "Found the -b option, with parameter value $param"
shift ;;
-c) echo "Found the -c option" ;;
--) shift
break ;;
*) echo "$1 is not an option";;
esac
shift

까다로운 하나, 나는 이것을 철저히 조사 할 것입니다. 예 : file : ///usr/share/doc/util-linux/examples/getopt-parse.bash, 매뉴얼 : man.jimmylandstudios.xyz/?man=getopt
JimmyLandStudios
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.