it-gundan.com

쉘 스크립트가 공백이나 다른 특수 문자에서 질식하는 이유는 무엇입니까?

또는 강력한 파일 이름 처리 및 셸 스크립트에서 전달되는 다른 문자열에 대한 소개 안내서입니다.

나는 대부분의 시간 동안 잘 작동하는 Shell 스크립트를 작성했다. 그러나 일부 입력 (예 : 일부 파일 이름)에서 질식합니다.

다음과 같은 문제가 발생했습니다.

  • 공백이 포함 된 파일 이름이 있습니다 hello world, 두 개의 별도 파일 helloworld로 처리되었습니다.
  • 두 개의 연속 공백이있는 입력 줄이 있고 입력 줄 하나가 줄었습니다.
  • 입력 행에서 선행 및 후행 공백이 사라집니다.
  • 때로는 입력에 문자 \[*?, 실제로는 파일 이름 인 일부 텍스트로 대체됩니다.
  • 아포스트로피가 있습니다 ' (또는 큰 따옴표 ") 입력에 들어가고 그 시점 이후에 일이 이상해졌습니다.
  • 입력에 백 슬래시가 있습니다 (또는 : Cygwin을 사용하고 있으며 일부 파일 이름에는 Windows 스타일 \ 구분 기호).

무슨 일이 있고 어떻게 해결합니까?

Gilles의 답변은 훌륭하지만 그의 주요 요점에서 문제를 제기합니다.

변수 대체 및 명령 대체에는 항상 큰 따옴표를 사용하십시오 : "$ foo", "$ (foo)"

Word splitting을 수행하는 Bash와 같은 Shell로 시작할 때 물론 안전한 조언은 항상 따옴표를 사용하는 것입니다. 그러나 단어 분리가 항상 수행되는 것은 아닙니다

§ 단어 분할

이 명령은 오류없이 실행될 수 있습니다

foo=$bar
bar=$(a command)
logfile=$logdir/foo-$(date +%Y%m%d)
PATH=/usr/local/bin:$PATH ./myscript
case $foo in bar) echo bar ;; baz) echo baz ;; esac

사용자가이 동작을 채택하도록 권장하지는 않지만 Word 분할이 발생하는시기를 누군가가 확실하게 이해하면 인용 부호를 사용할시기를 스스로 결정할 수 있어야합니다.

26
Steven Penny

파일 이름에 공백이 있고 디렉토리 이름에 공백이있는 큰 비디오 프로젝트가 있습니다. find -type f -print0 | xargs -0는 여러 가지 목적과 다양한 셸에서 작동하지만 bash를 사용하는 경우 사용자 지정 IFS (입력 필드 구분 기호)를 사용하면 유연성이 향상됩니다. 아래 스 니펫은 bash를 사용하고 IFS를 개행으로 설정합니다. 파일 이름에 줄 바꿈이 없다면 :

(IFS=$'\n'; for i in $(find -type f -print) ; do
    echo ">>>$i<<<"
done)

IFS의 재정의를 분리하기 위해 parens를 사용하는 것에 주목하십시오. IFS를 복구하는 방법에 대한 다른 게시물을 읽었지만 더 쉽습니다.

또한 IFS를 줄 바꿈으로 설정하면 셸 변수를 미리 설정하고 쉽게 인쇄 할 수 있습니다. 예를 들어 개행 문자를 구분 기호로 사용하여 변수 V를 점차적으로 증가시킬 수 있습니다.

V=""
V="./Ralphie's Camcorder/STREAM/00123.MTS,04:58,05:52,-vf yadif"
V="$V"$'\n'"./Ralphie's Camcorder/STREAM/00111.MTS,00:00,59:59,-vf yadif"
V="$V"$'\n'"next item goes here..."

그리고 이에 상응하여 :

(IFS=$'\n'; for v in $V ; do
    echo ">>>$v<<<"
done)

이제 큰 따옴표를 사용하여 줄 바꿈을 사용하여 echo "$V"로 V 설정을 "목록"할 수 있습니다. ($'\n' 설명의 경우 이 스레드 입니다.)

3
Russ

find directory -print0 | xargs -0를 사용하는 방법은 모든 스페셜을 처리해야합니다. 그러나 파일/디렉토리 당 하나의 PID가 필요하므로 성능 문제가 발생할 수 있습니다.

내가 최근에 접한 강력한 (및 성능이 뛰어난) 파일 처리의 다른 방법을 설명하겠습니다. 이는 find 출력을 탭으로 구분 된 CSV 데이터로 후 처리해야하는 경우에 적합합니다. 예를 들어 에 의해 AWK. 이러한 처리에서 실제로 파일 이름의 탭과 줄 바꿈 만 방해가됩니다.

디렉토리는 find directory -printf '%P\t///\n'를 통해 스캔됩니다. 경로에 탭이나 줄 바꿈이 포함되어 있지 않으면 경로 자체와 ///를 포함하는 필드라는 두 개의 CSV 필드가있는 하나의 레코드가 생성됩니다.

경로에 탭이 포함 된 경우 경로 조각 1, 경로 조각 2 및 ///를 포함하는 필드의 세 가지 필드가 있습니다.

줄 바꿈이 포함 된 경우 두 개의 레코드가 있습니다. 첫 번째 레코드는 경로 fragment1을 포함하고 두 번째 레코드는 경로 fragment2를 포함하고 필드는 ///를 포함합니다.

이제 중요한 사실은 ///가 경로에서 자연스럽게 발생할 수 없다는 것입니다. 또한 일종의 방수 탈출 장치 또는 터미네이터입니다.

find 출력을 스캔하고 until///를 찾은 (AWK) 프로그램을 작성하는 것도 가능합니다. 새 필드는 경로에서 탭이고 새 레코드는 경로에서 줄 바꿈입니다.

탭은 ///t로 안전하게 이스케이프 할 수 있으며 ///n는 파일 경로에서 자연스럽게 발생할 수 없다는 것을 알면 개행을 ///로 안전하게 이스케이프 할 수 있습니다. ///t///n를 다시 탭으로 변환하면 처리에서 일부 출력이 생성 될 때 마지막에 줄 바꿈이 발생할 수 있습니다.

예, 복잡하게 들리지만 단서에는 설명 된 알고리즘을 수행하는 findawk 인스턴스의 두 PID 만 필요하다는 것입니다. 그리고 빠릅니다.

아이디어는 내 것이 아닙니다. 디렉토리 동기화를 위해이 새로운 (2019) bash 스크립트에서 구현 된 것을 발견했습니다 : Zaloha.sh . 실제로 알고리즘을 설명하는 문서가 있습니다.

파일 이름의 특수 문자로 해당 프로그램을 중단하거나 질식시킬 수 없었습니다. 심지어 줄 바꿈과 탭만있는 디렉토리를 올바르게 처리했습니다 ...

0
user400462

위에서 언급 한 모든 보안 관련 사항을 고려하고 변수를 신뢰하고 제어 할 수 있다고 가정하면 eval를 사용하여 공백이있는 여러 경로를 가질 수 있습니다. 그러나 조심하십시오!

$ FILES='"a b" c'
$ eval ls $FILES
ls: a b: No such file or directory
ls: c: No such file or directory
$ FILES='a\ b c'
$ eval ls $FILES
ls: a b: No such file or directory
ls: c: No such file or directory
0
Mattias Wadman