![Bash for 루프, 문자열 var에 공백이 포함되어 있습니다.](https://linux55.com/image/14378/Bash%20for%20%EB%A3%A8%ED%94%84%2C%20%EB%AC%B8%EC%9E%90%EC%97%B4%20var%EC%97%90%20%EA%B3%B5%EB%B0%B1%EC%9D%B4%20%ED%8F%AC%ED%95%A8%EB%90%98%EC%96%B4%20%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4..png)
내 디렉토리에는 공백이 있는 두 개의 파일 foo bar
과 another file
공백이 없는 두 개의 파일이 있습니다 file1
.file2
다음 스크립트가 작동합니다.
for f in foo\ bar another\ file; do file "$f"; done
이 스크립트는 다음과 같은 경우에도 작동합니다.
for f in 'foo bar' 'another file'; do file "$f"; done
그러나 다음 스크립트는 작동하지 않습니다.
files="foo\ bar another\ file"
for f in $files; do file "$f"; done
이 스크립트도 작동하지 않습니다.
files="'foo bar' 'another file'"
for f in $files; do file "$f"; done
그러나 파일에 공백이 없으면 스크립트가 작동합니다.
files="file1 file2"
for f in $files; do file "$f"; done
감사해요!
편집하다
내 스크립트의 코드 조각:
while getopts "i:a:c:d:f:g:h" arg; do
case $arg in
i) files=$OPTARG;;
# ...
esac
done
for f in $files; do file "$f"; done
공백이 없는 파일의 경우 내 스크립트가 작동합니다. 하지만 다음 방법 중 하나로 공백이 포함된 파일을 매개변수로 전달하는 스크립트를 실행하고 싶습니다.
./script.sh -i "foo\ bar another\ file"
./script.sh -i foo\ bar another\ file
./script.sh -i "'foo bar' 'another file'"
./script.sh -i 'foo bar' 'another file'
답변1
다음을 사용하는 경우 bash
배열을 사용할 수 있습니다.
#!/bin/bash
files=('foo bar' 'another file' file1 'file2')
for f in "${files[@]}"; do file -- "$f"; done
공백이나 쉘 구문의 기타 특수 문자(예 ;
: , |
, 등)가 포함된 파일 이름은 따옴표로 묶어야 합니다.&
*
와일드카드( , , 및 일부 버전에서는 활성화된 경우 더 많음) 또는 문자(공백, 탭 및 줄 바꿈) 파일 이름 기본값을 포함하려면 최소한 목록 컨텍스트(in "${files[@]}"
및 here 와 같이)에서 확장 주위에 큰따옴표가 필요합니다 . ."$f"
*
?
[
\
bash
extglob
$IFS
이러한 문자를 포함하지 않고 ASCII가 아닌 문자를 포함하지 않는 파일 이름의 경우 선택 사항입니다(권장합니다).
파일 목록이 현재 작업 디렉터리에서 오는 경우 원하는 대로 와일드카드를 사용할 수 있습니다. 예를 들어 이름에 포함된 파일이나 디렉터리를 files=(*f*)
일치시키 려면 일치하지 않는 경우 리터럴을 미리 가져오는 것을 피하고 싶습니다. (그러나 배열을 완전히 사용하거나 피할 수도 있습니다).f
shopt -s nullglob
*f*
for f in *f*; do...done
--
태그는 file
대시로 시작하는 경우에도 후속 인수가 파일 이름임을 알려줍니다.
man bash
(검색 Arrays
)을 사용하거나 자세한 내용을 읽어보세요 info bash arrays
.
답변2
게시한 네 가지 스크립트 호출 간에는 약간의 차이점이 있습니다.
./script.sh -i "'foo bar' 'another file'"
./script.sh -i "foo\ bar another\ file"
위의 두 매개변수 모두 첫 번째 매개변수로 스크립트에 전달되고 -i
, 두 번째 매개변수로 단일 문자열이 전달됩니다. 첫 번째는 이고 'foo bar' 'another file'
, 두 번째는 입니다 foo\ bar another\ file
. 쉘 언어에서는 둘 다 두 개의 문자열(또는 파일 이름) foo bar
및 를 나타냅니다 another file
. 그러나 인용 및 백슬래시 처리는 문자열이 문자열로 끝나기 때문에 변수 내부에 있는 경우가 아니라 문자열이 원래 명령줄에 있는 경우에만 적용됩니다.
./script.sh -i foo\ bar another\ file
./script.sh -i 'foo bar' 'another file'
반면에, 이 두 사람은 총삼매개변수: -i
, foo bar
, 및 another file
.
다양한 매개변수를 안전하게 처리하는 것이 훨씬 쉽기 때문에 구별이 다소 중요합니다. 그대로 유지하면 그 안에 포함된 따옴표와 이스케이프를 처리할 필요가 없습니다.
또한 중요한 것은 유사한 작업을 실행하면 script ./*.txt
파일 이름이 다른 매개변수로 전달된다는 것입니다.
예를 들어, 다음과 같이 호출하면 file
두 개의 파일 만 호출됩니다 script 'foo bar' another\ file
.
#! /bin/sh -
for f in "$@"; do
file -- "$f"
done
또는 더 간단하고 이식성이 뛰어난 경우 for
기본적으로 위치 인수를 반복합니다.
#! /bin/sh -
for f do
file -- "$f"
done
하지만 getopts도 있습니다. 그리고 파일 이름을 다른 매개변수로 사용하면 첫 번째 이름만 사용됩니다 -i
. 여기에는 기본적으로 두 가지 공통 옵션이 있습니다.
사용자에게 -i
옵션을 재사용하여 파일 이름을 배열로 수집하도록 하십시오.
#! /bin/bash -
files=()
while getopts "i:" arg; do
case $arg in
(i) files+=("$OPTARG");;
esac
done
for f in "${files[@]}"; do file -- "$f"; done
script -i "foo bar" -i "another file"
(run은 script -i file1 file2
무시됩니다 . file2
) 마찬가지로 다른 옵션을 통해 제공된 파일 이름을 수집하기 위해 다른 배열을 추가할 수 있습니다.
또는 옵션이 스크립트가 작동하는 "모드"를 설정하고 파일 이름을 옵션과 다른 목록으로 갖도록 합니다. getopts
모든 매개변수를 그대로 유지하고 처리된 매개변수만 삭제하면 됩니다 shift
. 그래서:
#! /bin/bash -
unset -v mode
while getopts "ie" arg; do
case $arg in
(i) mode=i;;
(e) mode=e;;
(*) : handle usage error;;
esac
done
shift "$((OPTIND - 1))"
case "$mode" in
(i) for f do file -- "$f"; done;;
(e) echo "do something else with the files";;
(*) echo "error: mode not specified" >&2;;
esac
script -i "foo bar" "another file"
그런 다음 or script -i -- -file-starting-with-dash- -other-file-
또는 으로 실행하십시오 script -i -- *
.
getopts
여기서는 당신이 accept 이외의 다른 일을 했다고 가정합니다 -i
. 그렇지 않으면 플래그를 완전히 포기했을 수도 있기 때문입니다. :) 그러나 어떤 다른 옵션이 있는지에 따라 가장 합리적인(또는 관례적인) 솔루션이 어느 정도 영향을 받습니다.
또한 루프가 파일만 호출하는 경우 루프를 file
실행 file -- "${files[@]}"
하고 건너뛸 수 있습니다.file -- "$@"
그러나 이 작업을 수행하려면 다음을 수행하십시오.
script -i foo bar -e doo daa
스크립트가 파일에 대해 foo
한 가지 작업을 수행 하고 및 bar
에 대해 doo
다른 작업을 수행하게 daa
하면 다른 질문이 됩니다. 물론 할 수는 있지만 getopts
그것을 달성하기 위한 도구는 아닐 수도 있습니다.
또한보십시오:
- https://mywiki.wooledge.org/BashGuide/Arrays특히 배열의 경우
- 변수에 저장된 명령을 어떻게 실행할 수 있나요?제목은 명령에 관한 것이지만 배열에 대해서도 설명합니다.
그리고 물론:
단일 변수에서 여러 개의 다른 임의 문자열(파일 이름)을 처리하려고 할 때 발생하는 문제.
답변3
명령줄 구문 분석의 경우 경로 이름 피연산자가 항상 명령줄의 마지막 피연산자가 되도록 정렬합니다.
./myscript -a -b -c -- 'foo bar' 'another file' file[12]
옵션 구문 분석은 다음과 같습니다.
a_opt=false b_opt=false c_opt=false
while getopts abc opt; do
case $opt in
a) a_opt=true ;;
b) b_opt=true ;;
c) c_opt=true ;;
*) echo error >&2; exit 1
esac
done
shift "$(( OPTIND - 1 ))"
for pathname do
# process pathname operand "$pathname" here
done
shift
경로 이름 피연산자가 위치 인수 목록에 남아 있도록 처리된 옵션이 밖으로 이동되도록 합니다 .
이것이 가능하지 않은 경우 -i
옵션을 여러 번 지정하도록 허용하고 루프에서 해당 인수가 발견될 때마다 배열에 지정된 인수를 수집합니다.
pathnames=() a_opt=false b_opt=false c_opt=false
while getopts abci: opt; do
case $opt in
a) a_opt=true ;;
b) b_opt=true ;;
c) c_opt=true ;;
i) pathnames+=( "$OPTARG" ) ;;
*) echo error >&2; exit 1
esac
done
shift "$(( OPTIND - 1 ))"
for pathname in "${pathnames[@]}"; do
# process pathname argument "$pathname" here
done
이것은 불릴 것이다
./myscript -a -b -c -i 'foo bar' -i 'another file' -i file1 -i file2
답변4
Bash는 공백이 포함된 변수를 잘 처리하지 못합니다. 이 작업을 수행하려면 아래와 같이 "이름 바꾸기" 기능을 사용해야 합니다.
데비안과 유사한 시스템을 사용하는 경우 다음을 사용하십시오.
sudo apt-get install rename
Red Hat과 같은 시스템을 사용하는 경우 다음을 사용하십시오.
sudo yum install install rename
파일이 위치한 디렉토리로 이동
cd your_target_directory
공백 문자가 포함된 모든 파일/변수의 이름을 바꿉니다. 이 단계를 거치면 공간은 더 이상 문제가 되지 않습니다.
rename 's/ /_/g' *
여기서부터 계속하는 데 문제가 없습니다...즐기세요