파일 이름에 =가 포함되어 있으면 awk가 중지하고 기다리는 이유는 무엇이며 이 문제를 해결하는 방법은 무엇입니까?

파일 이름에 =가 포함되어 있으면 awk가 중지하고 기다리는 이유는 무엇이며 이 문제를 해결하는 방법은 무엇입니까?
awk 'processing_script_here' my=file.txt

끝없이 멈추고 기다리는 것...
여기서 무슨 일이 일어나고 있으며 어떻게 작동하게 만들까요?

답변1

~처럼크리스가 말한다, 양식의 인수는 입력 파일 이름이 아닌 변수 할당(문 이전에 수행되는 (최신) 변수 할당 variablename=anything과 달리 인수가 처리될 때 수행됨)로 처리됩니다 .-v var=valueBEGIN

이는 다음과 같은 경우에 유용합니다.

awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2

FS거기에서 파일 별로 서로 다른 것을 지정할 수 있습니다 RS. 또한 일반적으로 다음 용도로 사용됩니다.

awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2

더 안전한 버전은 다음과 같습니다.

awk 'NR==FNR{a[$0]; next}; {...}' file1 file2

file1(비어 있으면 아무런 효과가 없습니다)

=그러나 파일 이름에 문자가 포함되어 있으면 문제가 발생합니다.

=이제 이것은 첫 번째 남은 내용이 유효한 변수 이름인 경우에만 문제가 됩니다 awk.

에서는 유효한 변수 이름의 구성이 awk에서보다 더 엄격합니다 sh.

POSIX에서는 다음과 같아야 합니다.

[_a-zA-Z][_a-zA-Z0-9]*

이식 가능한 문자 세트의 문자만 사용하십시오. 그러나 /usr/xpg4/bin/awkSolaris 11은 최소한 이 점에서 호환되지 않으며 a-zA-Z뿐만 아니라 로케일의 모든 알파벳 문자를 변수 이름에 사용할 수 있습니다.

x+y=foo따라서 =baror 또는 같은 인수는 ./foo=bar여전히 할당이 아닌 입력 파일 이름으로 처리됩니다. 첫 번째 인수의 나머지 부분은 =유효한 변수 이름이 아니기 때문입니다. 구현 및 로캘 Stéphane=Chazelas.txt에 따라 "may" 또는 "not"과 같은 매개변수입니다 .awk

그렇기 때문에 awk를 사용할 때 다음을 사용하는 것이 좋습니다.

awk '...' ./*.txt

바꾸다

awk '...' *.txt

예를 들어, 파일 이름에 문자가 txt포함되지 않는다고 보장할 수 없는 경우 문제를 방지할 수 있습니다 =.

또한 다음을 -vfoo=bar.txt사용하는 경우 유사한 매개변수가 옵션으로 간주될 수 있습니다.

awk -f file.awk -vfoo=bar.txt

(1.28.0 이전의 busybox 버전에도 적용 가능합니다 awk '{code}' -vfoo=bar.txt.awk해당 오류 보고서).

다시 말하지만, ./*.txt이 문제는 다음을 사용하여 해결할 수 있습니다(접두사를 사용하면 다른 의미로 이해되는 ./파일을 호출할 때도 도움이 됩니다).-awk표준 입력대신에).

이는 이유

#! /usr/bin/awk -f

Shebangs는 실제로 작동하지 않습니다. 이러한 문제는 var=value다음을 통해 해결될 수 있지만고정명세서의 값 ARGV( ./접두사 추가) BEGIN:

#! /usr/bin/awk -f
BEGIN {
  for (i = 1; i < ARGC; i++)
    if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
      ARGV[i] = "./" ARGV[i]
}
# rest of awk script

이는 옵션에 도움이 되지 않습니다. 왜냐하면 해당 옵션은 스크립트 awk가 아닌 awk스크립트에 의해 표시되기 때문입니다.

이 접두사 사용 시 ./발생할 수 있는 외관상의 문제 중 하나 는 로 끝나는 것입니다. 그러나 원하지 않는 경우 FILENAME언제든지 이 접두사를 사용하여 제거할 수 있습니다.substr(FILENAME, 3)

GNU 구현은 awk옵션을 통해 이러한 모든 문제를 해결합니다 -E.

그 후 -Egawk는 스크립트 경로 awk( -여전히 stdin을 의미함)와 입력 파일 경로 목록만 예상합니다( -특별한 처리도 수행하지 않음).

이는 다음을 위해 설계되었습니다:

#! /usr/bin/gawk -E

인수 목록이 항상 입력 파일인 shebangs( ARGV명령문 내에서 목록을 자유롭게 편집 할 수 있다는 점에 유의하세요 BEGIN).

다음과 같이 사용할 수도 있습니다.

gawk -e '...awk code here...' -E /dev/null *.txt

후속 스크립트가 문자를 포함하더라도 항상 입력 파일로 처리되도록 하기 위해 -E빈 스크립트( )를 사용합니다 ./dev/null*.txt=

답변2

대부분의 awk 버전에서 실행될 프로그램 뒤에 오는 매개변수는 다음과 같습니다.

  1. 하나의 문서
  2. 테이블 할당x=y

파일 이름은 사례 #2로 해석되므로 awk는 여전히 stdin에서 무언가를 읽기를 기다리고 있습니다(파일 이름이 전달된 것을 감지하지 못하기 때문입니다).

이식 가능하게도 이 동작은POSIX에 문서화됨:

다음 두 가지 유형의 매개변수를 혼합할 수 있습니다.

  • 파일: 프로그램에 설정된 패턴과 일치하도록 읽어야 할 입력이 포함된 파일의 경로 이름입니다. 파일 피연산자가 지정되지 않거나 파일 피연산자가 "-"인 경우 표준 입력을 사용해야 합니다.
  • 할당: 이식 가능한 문자 집합의 밑줄 또는 알파벳 문자로 시작하는 피연산자(IEEE Std 1003.1-2001, 섹션 6.1 이식 가능한 문자 집합의 기본 정의 볼륨에 있는 표 참조), 그 뒤에 일련의 밑줄, 숫자가 옵니다. 및 "=" 문자가 뒤따르는 이식 가능한 문자 집합의 문자는 경로 이름이 아닌 변수 할당을 지정해야 합니다.

따라서 몇 가지 이식 가능한 옵션이 있습니다(#1이 아마도 가장 덜 방해가 될 것입니다).

  1. "이식 가능한 문자 집합의 밑줄 또는 알파벳 문자"가 아니기 awk ... ./my=file때문에 이를 회피하는 를 사용하세요 ..
  2. 표준 입력에 파일을 배치하는 데 사용됩니다 awk ... < my=file. 그러나 이는 여러 파일에서는 제대로 작동하지 않습니다.
  3. 임시로 파일에 대한 하드 링크를 만든 다음 사용하세요. 이와 같은 작업을 수행 ln my=file my_file하고 my_file정상적으로 사용할 수 있습니다. 복사는 수행되지 않으며 두 파일 모두 동일한 데이터 및 inode 메타데이터로 백업됩니다. 사용 후에는 해당 아이노드에 대한 참조 개수가 여전히 0보다 크기 때문에 생성된 링크를 삭제해도 안전합니다.

답변3

견적으로 이동멍청한 문서(추가 강조 사항 참고):

명령줄의 다른 인수는 일반적으로 지정된 순서대로 처리되는 입력 파일로 처리됩니다. 하지만,var=value 형식의 매개변수는 var 변수에 값을 할당하며 파일을 전혀 지정하지 않습니다.

명령이 중지되고 기다리는 이유는 무엇입니까? 형태로 있기 때문에awk 'processing_script_here' my=file.txt 지정된 파일이 없습니다.위의 정의에서 - my=file.txt는 변수 할당으로 해석되며 파일이 정의되지 않은 경우 stdin을 읽습니다( 이러한 명령의 awk가 시스템 호출을 기다리고 있다는 것도 awk분명합니다 .straceread(0,'...)

이 내용은 에도 기록되어 있습니다.POSIX awk 사양, 피연산자 섹션 및작업그것의 일부)

awk '{print foo}' foo=bar /etc/passwd/etc/passwd의 각 줄이 값을 인쇄하기 때문에 변수 할당은 분명합니다 . 그러나 경로나 전체 경로를 foo지정하면 작동합니다../foo=bar

실행 strace하고 awk '1' foo=bar확인 하면 cat foo=bar이것이 awk 특정 문제임을 알 수 있으며 execve는 파일 이름을 전달된 인수로 표시하므로 이 경우 쉘은 환경 변수 할당과 아무 관련이 없습니다.

awk '...script...' foo=bar또한 환경 변수 할당은 명령 전에 적용되어야 하므로 이로 인해 쉘이 환경 변수를 생성하지 않습니다. 바라보다POSIX 쉘 구문 규칙, 포인트 7. 추가적으로 이는 다음을 통해 확인할 수 있습니다.awk '{print ENVIRON["foo"]}' foo=bar /etc/passwd

관련 정보