변수 대체 및 명령 대체는 항상 큰 따옴표를 사용하십시오 "$foo"
."$(foo)"
$foo
인용 부호 를 사용하지 않으면 스크립트는 $(foo)
공백 또는를 포함하는 입력 또는 매개 변수 (또는 명령 출력 )를 질식 \[*?
시킵니다.
거기서, 당신은 독서를 멈출 수 있습니다. 자, 여기 몇 가지 더 있습니다 :
read
— 내장을 사용 하여 한 줄씩 입력을 읽으려면read
while IFS= read -r line; do …
Plain을 사용하여read
백 슬래시와 공백을 특별히 처리합니다.
xargs
— 피하십시오xargs
. 당신이 사용해야하는 경우 xargs
, 그것을 확인하십시오 xargs -0
. 대신 find … | xargs
, 좋아find … -exec …
.
xargs
공백과 문자를 \"'
특수하게 처리합니다 .
이 답변은 Bourne의 / POSIX 스타일의 쉘 (적용 sh
, ash
, dash
, bash
, ksh
, mksh
, yash
...). Zsh 사용자는이를 생략하고 큰 따옴표가 필요한시기 의 끝을 읽어야합니다 . 대신에. 전체 내용을 이해하려면 표준 설명서 나 쉘 설명서를 읽으십시오 .
아래 설명에는 몇 가지 근사치 (대부분의 조건에서 적용되지만 주변 환경이나 구성에 영향을받을 수있는 설명)이 포함되어 있습니다.
왜 써야 "$foo"
합니까? 따옴표 없이는 어떻게 되나요?
$foo
"변수의 값을 가져 간다"는 의미는 아닙니다 foo
. 훨씬 복잡한 것을 의미합니다.
- 먼저 변수 값을 가져옵니다.
- 필드 분할 : 해당 값을 공백으로 구분 된 필드 목록으로 취급하고 결과 목록을 작성하십시오. 변수가 포함되어 있으면, 예를 들면,
foo * bar
이 단계의 결과는 3 요소 목록 foo
, *
, bar
.
- 파일 이름 생성 : 각 필드를 glob, 즉 와일드 카드 패턴으로 취급하고이 패턴과 일치하는 파일 이름 목록으로 대체하십시오. 패턴이 파일과 일치하지 않으면 수정되지 않은 상태로 유지됩니다. 이 예에서는
foo
현재 디렉토리의 파일 목록 다음에을 포함하는 목록이 표시됩니다 bar
. 현재 디렉토리가 비어있는 경우, 결과는 foo
, *
, bar
.
결과는 문자열 목록입니다. 셸 구문에는 목록 컨텍스트와 문자열 컨텍스트의 두 가지 컨텍스트가 있습니다. 필드 분할 및 파일 이름 생성은 목록 컨텍스트에서만 발생하지만 대부분의 경우입니다. 큰 따옴표는 문자열 컨텍스트를 구분합니다. 큰 따옴표로 묶은 전체 문자열은 분할되지 않는 단일 문자열입니다. (예외 : "$@"
위치 매개 변수 목록으로 확장하는 경우 (예 : 위치 매개 변수가 세 개인 경우 "$@"
와 동일합니다 "$1" "$2" "$3"
. $ *와 $ @의 차이점 은 무엇입니까? )
$(foo)
또는 로 명령을 바꾸는 경우에도 마찬가지 입니다 `foo`
. 참고 `foo`
로 인용 규칙은 이상하고 이식성 이 없으며 모든 현대 쉘 $(foo)
은 직관적 인용 규칙을 제외하고는 절대적으로 동등한 것을 지원 합니다.
산술 대입의 결과도 동일한 확장을 거치지 만 확장 할 IFS
수없는 문자 만 포함하기 때문에 일반적으로 문제 가되지 않습니다 ( 숫자 또는를 포함하지 않는다고 가정 -
).
큰 따옴표는 언제 필요한가요?를 참조하십시오 . 따옴표를 생략 할 수있는 경우에 대한 자세한 내용은
이 모든 리 갈마 어가 발생하지 않는 한 항상 변수 및 명령 대체에 큰 따옴표를 사용해야합니다. 따옴표를 생략하면 오류뿐만 아니라 보안상의 허점이 생길 수 있습니다 .
파일 이름 목록을 어떻게 처리합니까?
myfiles="file1 file2"
공백을 포함하여 파일 을 쓰는 경우 공백이 포함 된 파일 이름으로는 작동하지 않습니다. 유닉스 파일 이름은 /
(항상 디렉토리 구분자 인) 이외의 문자 와 널 바이트 (대부분의 쉘이있는 쉘 스크립트에서는 사용할 수 없음)를 포함 할 수 있습니다.
와 같은 문제입니다 myfiles=*.txt; … process $myfiles
. 이렇게하면 변수 myfiles
에 5 자 문자열 이 포함되며 와일드 카드가 확장되었다고 *.txt
쓸 때 $myfiles
입니다. 이 예제는 스크립트를로 변경할 때까지 실제로 작동합니다 myfiles="$someprefix*.txt"; … process $myfiles
. 경우 someprefix
에 설정 final report
이 작동하지 않습니다.
파일 이름과 같은 모든 종류의 목록을 처리하려면 목록을 배열에 넣으십시오. 여기에는 mksh, ksh93, yash 또는 bash가 필요합니다 (또는 모든 인용 문제가없는 zsh). 일반 POSIX 셸 (예 : 애쉬 또는 대시)에는 배열 변수가 없습니다.
myfiles=("$someprefix"*.txt)
process "${myfiles[@]}"
Ksh88에는 다른 할당 구문이있는 배열 변수가 있습니다 set -A myfiles "someprefix"*.txt
( ksh88 / bash 이식성이 필요한 경우 다른 ksh 환경에서 할당 변수 참조 ). Bourne / POSIX 스타일 쉘에는 하나의 단일 배열이 있으며, 여기에는 "$@"
사용자가 설정 한 위치 매개 변수 배열이 set
있으며 함수에 대해 로컬입니다.
set -- "$someprefix"*.txt
process -- "$@"
로 시작하는 파일 이름은 -
어떻습니까?
관련 메모에서 파일 이름은 -
(대시 / 빼기)로 시작할 수 있으며 대부분의 명령은 옵션을 나타내는 것으로 해석합니다. 변수 부분으로 시작하는 파일 이름이있는 경우 --
위의 스 니펫에서 와 같이 파일 이름을 먼저 전달하십시오 . 이것은 명령이 옵션의 끝에 도달했음을 나타내므로 그 이후의 모든 항목은로 시작하더라도 파일 이름입니다 -
.
또는 파일 이름이 이외의 다른 문자로 시작되도록 할 수 있습니다 -
. 절대 파일 이름 은로 시작하고 상대 이름의 시작 부분에 /
추가 할 수 있습니다 ./
. 다음 스 니펫은 변수의 내용을 f
로 시작하지 않는 동일한 파일을 참조하는 "안전한"방법으로 바꿉니다 -
.
case "$f" in -*) "f=./$f";; esac
이 주제에 대한 마지막 참고 사항에서 일부 명령은 -
이후에도 표준 입력 또는 표준 출력을 의미하는 것으로 해석 됩니다 --
. 이라는 실제 파일을 참조해야 -
하거나 그러한 프로그램을 호출하는 경우 stdin에서 읽거나 stdout에 쓰지 않으려면 -
위와 같이 다시 작성하십시오 . "du-sh *"와 "du -sh ./*"의 차이점 은 무엇입니까?를 참조하십시오 . 추가 토론을 위해.
변수에 명령을 어떻게 저장합니까?
“명령”은 명령 이름 (전체 경로가 있거나없는 실행 파일 이름, 함수 이름, 내장 또는 별명), 인수가있는 명령 이름 또는 쉘 코드의 세 가지를 의미 할 수 있습니다. 따라서 변수에 저장하는 방법에는 여러 가지가 있습니다.
명령 이름이 있으면 저장하고 평소처럼 큰 따옴표와 함께 변수를 사용하십시오.
command_path="$1"
…
"$command_path" --option --message="hello world"
인수가있는 명령이있는 경우 문제는 위의 파일 이름 목록과 동일합니다. 이는 문자열이 아니라 문자열 목록입니다. 공백을 사이에두고 하나의 문자열에 인수를 넣을 수는 없습니다. 그렇게하면 인수의 일부인 공백과 인수를 구분하는 공백의 차이를 알 수 없기 때문입니다. 쉘에 배열이 있으면 사용할 수 있습니다.
cmd=(/path/to/executable --option --message="hello world" --)
cmd=("${cmd[@]}" "$file1" "$file2")
"${cmd[@]}"
배열없이 쉘을 사용한다면 어떨까요? 위치 매개 변수를 수정하지 않아도 여전히 위치 매개 변수를 사용할 수 있습니다.
set -- /path/to/executable --option --message="hello world" --
set -- "$@" "$file1" "$file2"
"$@"
방향 전환, 파이프 등 복잡한 쉘 명령을 저장해야하는 경우 어떻게해야합니까? 또는 위치 매개 변수를 수정하지 않으려는 경우? 그런 다음 명령이 포함 된 문자열을 작성하고 eval
내장을 사용할 수 있습니다 .
code='/path/to/executable --option --message="hello world" -- /path/to/file1 | grep "interesting stuff"'
eval "$code"
의 정의에서 중첩 된 따옴표를 참고 code
: 작은 따옴표는 '…'
변수의 값이 있도록 리터럴 문자열을 구분하는 code
문자열입니다 /path/to/executable --option --message="hello world" -- /path/to/file1
. eval
내장은 스크립트에서 나타난 것처럼 인수로 전달 된 문자열을 구문 분석하는 쉘을 알려줍니다, 그래서 그 시점에서 따옴표 및 파이프 등, 구문 분석
사용 eval
이 까다 롭습니다. 언제 구문 분석 될지 신중하게 생각하십시오. 특히 파일 이름을 코드에 넣을 수는 없습니다. 소스 코드 파일에있는 것처럼 파일 이름을 인용해야합니다. 그렇게 할 직접적인 방법은 없습니다. 같은 뭔가 code="$code $filename"
파일 이름에 쉘 특수 문자가 포함 된 경우 휴식 (공간, $
, ;
, |
, <
, >
, 등). code="$code \"$filename\""
아직도에 끊는다 "$\`
. 심지어 code="$code '$filename'"
파일 이름이 포함 된 경우 나누기 '
. 두 가지 해결책이 있습니다.
파일 이름 주위에 따옴표 레이어를 추가하십시오. 가장 쉬운 방법은 주위에 작은 따옴표를 추가하고 작은 따옴표를로 바꾸는 것 '\''
입니다.
quoted_filename=$(printf %s. "$filename" | sed "s/'/'\\\\''/g")
code="$code '${quoted_filename%.}'"
코드 내에서 변수 확장을 유지하여 코드 조각이 빌드 될 때가 아니라 코드가 평가 될 때 조회되도록합니다. 이것은 더 간단하지만 코드가 실행될 때 변수가 여전히 같은 값으로 주위에있는 경우에만 작동합니다 (예 : 코드가 루프에 빌드 된 경우).
code="$code \"\$filename\""
마지막으로 코드를 포함하는 변수가 정말로 필요합니까? 코드 블록에 이름을 지정하는 가장 자연스러운 방법은 함수를 정의하는 것입니다.
무슨 일이야 read
?
이없는 -r
경우 read
연속 행을 허용합니다. 이것은 단일 논리 입력 행입니다.
hello \
world
read
입력 행을 문자로 구분 된 필드로 분리합니다 $IFS
(없이 -r
백 슬래시는 이스케이프 처리). 입력 세 단어를 포함하는 라인 인 경우에는 예를 들어, read first second third
설정 first
, 입력 단어의 처음에 second
두 번째 워드와 third
세번째 단어로. 단어가 더 있으면 마지막 변수에는 이전 단어를 설정 한 후 남은 모든 것이 포함됩니다. 선행 및 후행 공백이 잘립니다.
IFS
빈 문자열로 설정 하면 트리밍을 피할 수 있습니다. IFS = 대신 왜 IFS = 읽기가 자주 사용 되는가?를 참조하십시오 . 읽는 동안 ..`? 더 긴 설명을 위해.
무슨 일이야 xargs
?
입력 형식 xargs
은 공백으로 구분 된 문자열이며 선택적으로 작은 따옴표 나 큰 따옴표로 묶을 수 있습니다. 이 형식은 표준 도구로 출력되지 않습니다.
xargs -L1
또는 xargs -l
줄에 대한 입력 은 거의 줄 목록이지만 줄 끝에 공백이 있으면 다음 줄은 연속 줄입니다.
당신이 사용할 수있는 xargs -0
적용 가능한 (그리고 가능한 경우 : GNU (리눅스, Cygwin에서), 비지 박스, BSD, OSX하지만 POSIX 아닙니다). null 바이트는 대부분의 데이터, 특히 파일 이름에 나타날 수 없기 때문에 안전합니다. 널로 구분 된 파일 이름 목록을 생성하려면를 사용하십시오 find … -print0
(또는 find … -exec …
아래 설명 된대로 사용할 수 있음 ).
찾은 파일은 어떻게 처리 find
합니까?
find … -exec some_command a_parameter another_parameter {} +
some_command
외부 명령이어야하며 셸 함수 나 별칭 일 수 없습니다. 파일을 처리하기 위해 쉘을 호출해야하는 경우 sh
명시 적으로 호출하십시오 .
find … -exec sh -c '
for x do
… # process the file "$x"
done
' find-sh {} +
다른 질문이 있습니다
이 사이트 에서 인용 태그 또는 쉘 또는 쉘 스크립트를 찾아보십시오 . (“자세히 알아보기…”를 클릭하면 몇 가지 일반적인 팁과 손으로 선택하는 일반적인 질문 목록을 볼 수 있습니다.) 검색했지만 답변을 찾을 수없는 경우, 문의하십시오 .
shellcheck
프로그램의 품질을 향상시키는 데 도움이됩니다.