AWK 스크립트 내에서 변수를 외부 유틸리티에 인수로 전달할 수 있습니다.
awk 'BEGIN {
filename = "path_to_file_without_space"
"file " filename | getline
print $0
}'
그러나 변수에 공백이 포함되어 있으면
awk 'BEGIN {
filename = "path to file with spaces"
"file " filename | getline
print $0
}'
오류가 발생했습니다.
file: cannot open `path' (No such file or directory)
쉘이 공백에서 인용되지 않은 변수를 분할하는 방법과 마찬가지로 공백에서 인수를 분할하는 것이 좋습니다. 다음과 같이 쉘의 IFS를 null로 설정하여 쉘 필드 분할을 비활성화하고 싶습니다.
"IFS= file " filename | getline
또는 AWK 명령을 실행하기 전에 IFS를 null로 설정했지만 두 옵션 모두 차이가 없습니다. 이 필드 분할을 피하는 방법은 무엇입니까?
답변1
파일 이름을 인용해야 합니다.
awk 'BEGIN {
filename = "path to file with spaces"
"file \"" filename "\"" | getline
print
}'
또는 댓글에서 제안한 대로 더 쉽게 읽을 수 있도록
awk 'BEGIN {
DQ = "\042" # double quote (ASCII octal 42)
filename = "path to file with spaces"
"file " DQ filename DQ | getline
print
}'
또는 이것이 awk
더 큰 프로그램의 일부라고 가정하면,
BEGIN {
SQ = "\047"
DQ = "\042"
}
BEGIN {
name = "filename with spaces"
cmd = sprintf("file %s%s%s", DQ, name, DQ)
cmd | getline
close(cmd)
print
}
즉, 열린 파일 핸들 저장이 완료되면 명령을 닫습니다. 별도의 블록에 편리한 "상수"를 설정합니다 BEGIN
(이러한 블록은 순차적으로 실행됩니다). 별도의 변수를 사용하여 명령을 만듭니다 sprintf
. (이 내용의 대부분은 분명히 awk
유지 관리를 위해 읽을 수 있는 구조를 제공해야 하는 더 길거나 복잡한 프로그램을 대상으로 합니다 . dquote()
문자열을 참조하는 및 함수를 작성하는 것을 상상할 수도 있습니다.)squote()
"파이프"의 왼쪽은 리터럴 문자열로 평가됩니다.
file "path to file with spaces"
기본적으로 using은 문자열인 단일 매개변수를 사용 cmd | getline
하여 awk
호출 합니다 . 따라서 실행을 사용하려면 문자열을 올바르게 인용해야 합니다 .sh -c
cmd
sh -c
기술적인 세부 사항은 다음을 참조하세요.POSIX 표준:
expression | getline [var]
명령 출력이 파이프되는 스트림에서 입력 레코드를 읽습니다. 스트림이 현재 열려 있지 않으면
expression
명령 이름으로 값을 사용하여 생성해야 합니다. 생성된 스트림은popen()
표현식의 값을 명령 인수로, 값을r
인수 로 사용하여 함수를 호출하여 생성된 스트림 과 동일해야 합니다mode
. 스트림이 열려 있는 한expression
동일한 문자열 값으로 평가되는 후속 호출은 스트림에서 후속 레코드를 읽어야 합니다.close
동일한 문자열 값으로 평가되는 표현식을 사용하여 함수가 호출될 때까지 스트림은 열린 상태로 유지되어야 합니다 . 그 때 이 함수를 호출한 것처럼 스트림이 닫힙니다pclose()
.var
생략 되면 설정$0
되고NF
, 그렇지 않으면var
설정되고, 해당하는 경우 숫자 문자열로 처리됩니다(awk의 표현식 참조).
popen()
여기서 언급되는 함수는 C 라이브러리 popen()
함수입니다. 이것은 실행을 위해 주어진 문자열을 예약합니다 sh -c
.
system()
공백이 포함된 파일 이름으로 명령을 실행하면 정확히 동일한 문제가 발생하지만 이 경우 system()
C 라이브러리의 함수가 호출됩니다.반품호출 sh -c
방법은 비슷합니다 popen()
(그러나 I/O 스트림의 파이프라인은 다릅니다).
따라서 단일 인수로 호출하면 IFS
설정이 도움이 되지 않습니다.sh -c
file path to file with spaces
답변2
파일 이름에 관계없이 공백은 걱정거리가 가장 적습니다. 예를 들어, $(reboot)
또는 foo;reboot #whatever
또는 ...이라는 foo|reboot|bar
파일을 생각해 보십시오.
awk
명령줄 을 해석하기 위해 호출되므로 sh
임의 입력에서 명령줄을 작성할 때 명령 주입 취약점을 방지하기 위해 매개변수를 적절하게 이스케이프하는 것이 중요합니다.cmdline | getline
print | cmdline
system(cmdline)
쉘에서 인용하는 것은 까다로운 일입니다. 쉘에는 다양한 인용 연산자( '...'
, "..."
, \
, $'...'
, $"..."
) 가 있지만 '...'
이스케이프되지 않으므로 안전하지 않을 수 있습니다.모든특히 \
해당 인코딩은 일부 문자 세트의 다른 문자 인코딩에도 존재하므로 위험한 문자를 이스케이프하지 않습니다.
또한 쉘 코드에서 이전 형태의 명령 대체를 사용하지 않는 것이 중요합니다 `...`
. 이는 다른 수준의 백슬래시 처리를 도입하기 때문입니다.
환경 변수에 임의의 파일 이름이 있다고 가정합니다.
#! /bin/sh -
FILE="${1?No file provided}"
export FILE
awk -v q="'" '
function shquote(s) {
gsub(q, "&\"&\"&", s)
return q s q
}
BEGIN {
cmdline = "file -- " shquote(ENVIRON["FILE"])
if ((cmdline | getline) > 0)
print "The first line of \""cmdline"\" output was \""$0"\"."
else
print "Could not read a line from \""cmdline"\" output."
if (close(cmdline) != 0)
print cmdline" failed."
}'
위에서는 shquote()
문자열을 인수로 사용하고 sh
작은 따옴표(가장 안전한 따옴표 유형)로 묶어 인용하지만 문자열 자체의 작은 따옴표는 로 변경됩니다 '"'"'
. 즉, end '
, 그 뒤에 따옴표 '
, 또 다른 다시 열린 따옴표 "..."
가 이어집니다. '
다른 작은따옴표로 묶인 문자열의 경우.
위에서는 다른 가능한 경고를 확인할 수 있습니다.
--
파일 이름이 로 끝나는지 확인하려면 이 필요합니다-
.- 이 명령의 출력은
file
특히 파일 이름 자체에 개행 문자가 포함된 경우 한 줄에 출력된다는 보장이 없습니다. 결국 개행 문자는 파일 이름의 모든 문자만큼 유효합니다.getline
하나의 레코드만 읽으며 기본 레코드는 행입니다. 바라보다awk의 후루룩 모드?전체 출력을 읽는 방법에 대한 팁. - 출력에 줄이 전혀 포함되지 않을 수도 있습니다. 비어 있는 첫 번째 행에서 이를 보려면 의 반환 값을 확인해야 합니다
getline
. - 명령의 종료 상태를 확인하고 필요한 경우 문제를 보고하는 것이 좋습니다. 이는 반환된 값을 확인하여 수행됩니다
close()
. 그러나awk
이 값이 종료 상태를 인코딩하는 방법에 따라 구현이 다릅니다. 유일한 공통점은 명령이 성공하면(0 종료 코드로 종료) 값이 0이라는 것입니다.