Bash 스크립트에서 인수를 반복하는 방법
쉘 / bash 스크립트를 만들고 싶은 복잡한 명령이 있습니다. $1
쉽게 쓸 수 있습니다.
foo $1 args -o $1.ext
여러 입력 이름을 스크립트에 전달할 수 있기를 원합니다. 그것을하는 올바른 방법은 무엇입니까?
그리고 물론 공백이있는 파일 이름을 처리하고 싶습니다.
"$@"
모든 인수를 나타내는 데 사용 합니다.
for var in "$@"
do
echo "$var"
done
이것은 각 인수를 반복하고 별도의 행에 인쇄합니다. $ @는 인용 될 때 공백이 있으면 인수가 올바르게 분할된다는 점을 제외하면 $ *처럼 작동합니다.
sh test.sh 1 2 '3 4'
1
2
3 4
VonC 가 지금 삭제 한 답변 을 다시 작성합니다 .
Robert Gamble의 간결한 대답은 질문을 직접적으로 다룹니다. 이것은 공백을 포함하는 파일 이름과 관련된 몇 가지 문제를 증폭시킵니다.
참조 : / bin / sh의 $ {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'
. 그러나 쉘은 경로 이름에 공백이나 줄 바꿈 또는 탭을 사용하지 않도록 강력히 권장합니다. 또한 표준 Unix 파일 이름에 공백 등이 포함되지 않는 이유입니다.
공백과 기타 성가신 문자를 포함 할 수있는 파일 이름을 다룰 때는 매우주의해야합니다. 오래 전에 Unix에서 표준이 아닌 프로그램이 필요하다는 것을 알게되었습니다. 나는 그것을 부른다 escape
(버전 1.1은 1989-08-23T16 : 01 : 45Z 일자).
다음은 escape
SCCS 제어 시스템과 함께 사용 되는 예입니다 . delta
( 체크인 생각 )과 get
( 체크 아웃 생각 )을 모두 수행하는 표지 스크립트입니다 . 특히 -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"
(요즘에는 이스케이프를 아주 철저히 사용하지 않을 것입니다. -e
예를 들어 인수 에는 필요하지 않습니다. 하지만 전반적으로 이것은를 사용하는 더 간단한 스크립트 중 하나입니다 escape
.)
The escape
program simply outputs its arguments, rather like echo
does, but it ensures that the arguments are protected for use with eval
(one level of eval
; I do have a program which did remote shell execution, and that needed to escape the output of escape
).
$ escape $var
'"./my' 'dir"' '"./anotherdir"'
$ escape "$var"
'"./my dir" "./anotherdir"'
$ escape x y z
x y z
$
I have another program called al
that lists its arguments one per line (and it is even more ancient: version 1.1 dated 1987-01-27T14:35:49). It is most useful when debugging scripts, as it can be plugged into a command line to see what arguments are actually passed to the command.
$ echo "$var"
"./my dir" "./anotherdir"
$ al $var
"./my
dir"
"./anotherdir"
$ al "$var"
"./my dir" "./anotherdir"
$
[Added: And now to show the difference between the various "$@"
notations, here is one more example:
$ 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
$
Notice that nothing preserves the original blanks between the *
and */*
on the command line. Also, note that you can change the 'command line arguments' in the shell by using:
set -- -new -opt and "arg with space"
This sets 4 options, '-new
', '-opt
', 'and
', and 'arg with space
'.
]
Hmm, that's quite a long answer - perhaps exegesis is the better term. Source code for escape
available on request (email to firstname dot lastname at gmail dot com). The source code for al
is incredibly simple:
#include <stdio.h>
int main(int argc, char **argv)
{
while (*++argv != 0)
puts(*argv);
return(0);
}
That's all. It is equivalent to the test.sh
script that Robert Gamble showed, and could be written as a shell function (but shell functions didn't exist in the local version of Bourne shell when I first wrote al
).
Also note that you can write al
as a simple shell script:
[ $# != 0 ] && printf "%s\n" "$@"
The conditional is needed so that it produces no output when passed no arguments. The printf
command will produce a blank line with only the format string argument, but the C program produces nothing.
Note that Robert's answer is correct, and it works in sh
as well. You can (portably) simplify it even further:
for i in "$@"
is equivalent to:
for i
I.e., you don't need anything!
Testing ($
is command prompt):
$ 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
I first read about this in Unix Programming Environment by Kernighan and Pike.
In bash
, help for
documents this:
for NAME [in WORDS ... ;] do COMMANDS; done
If
'in WORDS ...;'
is not present, then'in "$@"'
is assumed.
For simple cases you can also use shift
. It treats the argument list like a queue. Each shift
throws the first argument out and the index of each of the remaining arguments is decremented.
#this prints all arguments
while test $# -gt 0
do
echo "$1"
shift
done
You can also access them as an array elements, for example if you don't want to iterate through all of them
argc=$#
argv=("$@")
for (( j=0; j<argc; j++ )); do
echo "${argv[j]}"
done
aparse() {
while [[ $# > 0 ]] ; do
case "$1" in
--arg1)
varg1=${2}
shift
;;
--arg2)
varg2=true
;;
esac
shift
done
}
aparse "$@"
참고URL : https://stackoverflow.com/questions/255898/how-to-iterate-over-arguments-in-a-bash-script
'Development Tip' 카테고리의 다른 글
onActivityResult가 Fragment에서 호출되지 않습니다. (0) | 2020.09.28 |
---|---|
REST API / 웹 서비스 보안을위한 모범 사례 (0) | 2020.09.28 |
Java에서 문자열이 숫자인지 확인하는 방법 (0) | 2020.09.28 |
명령 줄에서 MongoDB 데이터베이스를 삭제하려면 어떻게해야합니까? (0) | 2020.09.28 |
루비의 클래스 << 자기 관용구 (0) | 2020.09.28 |