Bash 함수 인수 - 파일 이름의 공백

Bash 함수 인수 - 파일 이름의 공백

참조 매개변수에 대한 규칙이 있다는 것을 알고 있지만, 아무리 노력해도 이를 작동시키는 방법을 알 수 없는 것 같습니다.

단일 파일 이름을 인수로 받아들인 다음 해당 파일 이름을 사용하여 명령줄을 작성하고 명령줄을 실행하는 함수가 있습니다. 파일에 공백이 없으면 작동하게 할 수 있지만 공백이 있는 파일 이름이 도입되면 작동이 중단됩니다. 공백을 사용하여 작동시키려고 시도한 모든 것이 작동하지 않거나 공백이 없는 파일에서도 작동하지 않게 되었습니다.

다음은 공백이 없는 예제 스크립트입니다.

#!/bin/bash
list_files() {
    file="${1}"
    cmd="cat ${file}"
    echo "Running command: '${cmd}'"
    ${cmd}
}

TARGET_DIR="${1}"

while IFS= read -d $'\0' -r file; do
    list_files "${file}"
done < <(find ${TARET_DIR} -type f -print 0)

방금 다음 구조의 디렉터리를 만들었습니다.

./files/
|-- one                # ==> Contains the text "files/one"
|-- two                # ==> Contains the text "files/two"
|-- three and four     # ==> Contains the text "files/three and four"
|-- five               # ==> Contains the text "files/five"

위 디렉터리에서 위 스크립트를 실행하면 다음과 같은 결과가 나타납니다.

bash-3.2$ ./test.bash ./files
Running Command: 'cat ./files/one'
files/one
Running Command: 'cat ./files/two'
files/two
Running Command: 'cat ./files/three and four'
cat: ./files/three: No such file or directory
cat: and: No such file or directory
cat: four: No such file or directory
Running Command: 'cat ./files/five'
files/five

내가 뭘 잘못했나요? 파일 이름에 관계없이 이와 같은 스크립트가 작동하도록 하려면 어떻게 해야 합니까?

참고: 이 예에서는 간단합니다. 먼저 명령을 변수에 저장하지 않고 인쇄한 다음 명령 자체를 두 번 입력하여 호출합니다. 그러나 실제 스크립트의 경우 명령이 훨씬 더 복잡합니다. 나중에 만들어야 할 경우 한 곳에 보관하면 됩니다.

답변1

바꾸다:

cmd="cat ${file}"
printf '%s\n' "Running command: '${cmd}'"
${cmd}

eval쉘 코드로 해석되도록 고정된 명령을 전달하십시오 .

cmd='cat -- "$file"'
printf '%s\n' "Running command: '${cmd}'"
eval "$cmd"

Running command: cat -- "$file"아마도 원하는 결과 가 출력되지 않을 것입니다.

또는 ( 구체적으로) 확장이 다음으로 전달되도록 bash올바른 printf %q인용문을 사용하십시오 .$fileeval

printf -v cmd 'cat -- %q' "$file"
printf '%s\n' "Running command: $cmd"
eval "$cmd"

또는 여기에 간단한 명령이 있으므로 해당 간단한 명령의 매개변수를 배열에 저장할 수 있습니다.

cmd=(cat -- "$file")
printf 'Running command: '
printf ' %q' "${cmd[@]}"
printf '\n'
"${cmd[@]}"

$file유사한 문자를 포함하지 않는 것으로 보장되는 특정 문자를 알고 있는 경우 :( :이 문자는 Unix의 파일 이름에서 완벽하게 유효한 문자임에 유의하세요) 다음을 수행할 수 있습니다.

cmd="cat:--:$file"
IFS=:  # split on :
set -f # disable glob
# use the split+glob operator by leaving the variable unquoted:
printf 'Running command: '
printf ' %q' $cmd
printf '\n'
$cmd

이제 이 특정한 경우에는 다음을 수행할 수도 있습니다.

(PS4='Running command: '
set -x; cat -- "$file")

(메시지는 stderr로 전송됩니다.)

심지어:

PS4='Running command: ' find "$TARGET_DIR" -type f -exec sh -xc '
  cat "$1"' sh {} \;

sh또는 너무 많은 명령 을 실행하지 않으려면 /opt/ast/bin/cat에 매핑된 내장 함수가 있는 곳을 cat사용 하고 구문을 사용하여 가능한 한 적은 수의 쉘을 실행하십시오.ksh93cat{} +

PS4='Running command: ' find "$TARGET_DIR" -type f -exec ksh93 -c '
  PATH=/opt/ast/bin; set -x; for file do cat "$file"; done' ksh93 {} +

헤더와 함께 파일 내용을 표시하고 가능한 한 적은 수의 명령을 실행하려면 GNU 시스템에서 다음을 사용할 수도 있습니다 head.

$ find "$TARGET_DIR" -type f -exec head -vc-0 {} +
==> ./file 1 <==
1
2

==> ./file 2 <==
test

그리고 zsh:

zmodload zsh/mapfile
for file (**/*(D.)) printf '==> %q <==\n%s\n' $file $mapfile[$file]

관련 정보