루프를위한 공백이있는 파일 이름 찾기 명령


33

여러 하위 폴더의 모든 파일을 검색하고 tar로 아카이브하는 스크립트가 있습니다. 내 스크립트는

for FILE in `find . -type f  -name '*.*'`
  do
if [[ ! -f archive.tar ]]; then

  tar -cpf archive.tar $FILE
else 
  tar -upf archive.tar $FILE 
fi
done

find 명령은 다음과 같은 출력을 제공합니다

find . -type f  -iname '*.*'
./F1/F1-2013-03-19 160413.csv
./F1/F1-2013-03-19 164411.csv
./F1-FAILED/F2/F1-2013-03-19 154412.csv
./F1-FAILED/F3/F1-2011-10-02 212910.csv
./F1-ARCHIVE/F1-2012-06-30 004408.csv
./F1-ARCHIVE/F1-2012-05-08 190408.csv

그러나 FILE 변수는 경로의 첫 번째 부분 ./F1/F1-2013-03-19 만 저장 하고 다음 부분은 160413.csv 만 저장 합니다.

readwhile 루프와 함께 사용해 보았습니다 .

while read `find . -type f  -iname '*.*'`;   do ls $REPLY; done

하지만 다음과 같은 오류가 발생합니다

bash: read: `./F1/F1-2013-03-19': not a valid identifier

누구든지 다른 방법을 제안 할 수 있습니까?

최신 정보

아래 답변에서 제안한대로 스크립트를 업데이트했습니다.

#!/bin/bash

INPUT_DIR=/usr/local/F1
cd $INPUT_DIR
for FILE in "$(find  . -type f -iname '*.*')"
do
archive=archive.tar

        if [ -f $archive ]; then
        tar uvf $archive "$FILE"
        else
        tar -cvf $archive "$FILE"
        fi
done

내가 얻는 출력은

./test.sh
tar: ./F1/F1-2013-03-19 160413.csv\n./F1/F1-2013-03-19 164411.csv\n./F1/F1-2013-03-19 153413.csv\n./F1/F1-2013-03-19 154412.csv\n./F1/F1-2012-09-10 113409.csv\n./F1/F1-2013-03-19 152411.csv\n./.tar\n./F1-FAILED/F3/F1-2013-03-19 154412.csv\n./F1-FAILED/F3/F1-2013-03-19 170411.csv\n./F1-FAILED/F3/F1-2012-09-10 113409.csv\n./F1-FAILED/F2/F1-2011-10-03 113911.csv\n./F1-FAILED/F2/F1-2011-10-02 165908.csv\n./F1-FAILED/F2/F1-2011-10-02 212910.csv\n./F1-ARCHIVE/F1-2012-06-30 004408.csv\n./F1-ARCHIVE/F1-2011-08-17 133905.csv\n./F1-ARCHIVE/F1-2012-10-21 154410.csv\n./F1-ARCHIVE/F1-2012-05-08 190408.csv: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors

4
IFS=$'\n'`for 루프 전에 각 라인별로 파싱하도록 설정 해야합니다
kiri

답변:


36

forwith를 사용 하는 find것은 잘못된 접근 방식입니다. 예를 들어 열려는 웜 캔에 대한 이 글을 참조하십시오 .

권장되는 방법은 사용하는 것입니다 find, while그리고 read같은 설명 여기 . 다음은 귀하에게 적합한 예입니다.

find . -type f -name '*.*' -print0 | 
while IFS= read -r -d '' file; do
    printf '%s\n' "$file"
done

이렇게하면 파일 이름을 널 ( \0) 문자로 구분할 수 있습니다. 즉, 공백 및 기타 특수 문자의 변형으로 인해 문제가 발생하지 않습니다.

find찾은 파일로 아카이브를 업데이트하기 위해 출력을 tar다음으로 직접 전달할 수 있습니다 .

find . -type f -name '*.*' -printf '%p\0' | 
tar --null -uf archive.tar -T -

아카이브가 존재하는지 여부를 구별 할 필요는 없으며, tar적절하게 처리합니다. 또한 아카이브에 비트가 -printf포함되지 않도록 여기를 사용 하십시오 ./.


고마워, 그것은 거의 작동합니다. 유일한 것은 ./타르를 보관하는 것입니다 . ./.tar tar: ./archive.tar: file is the archive; not dumped
Ubuntuser

@Ubuntuser 당신은 볼 간단한 체크를 추가 할 수 있습니다if [[ "$FILE" == "./" ]]; then continue
kiri

@ Ubuntuser : 업데이트 된 답변 ./-printf참조 하여 비트를 피할 수 있습니다 . 그러나 현재 디렉토리를 참조하기 때문에 포함되거나 포함되지 않은 경우에는 차이가 없습니다. 또한 find/tar당신이 사용하고 싶을 수도 있는 대체 조합을 포함 시켰습니다 .
Thor

sort결과를 반복하기 전에 결과를 얻으 려는 sort -z경우 널 구분 기호 가 필요 합니다.
Adambean

13

인용을 시도하십시오 for 과 같이 루프를 .

for FILE in "`find . -type f  -name '*.*'`"   # note the quotation marks

따옴표가 없으면 bash는 공백과 줄 바꿈 ( \n)을 전혀 처리하지 못합니다 ...

또한 설정을 시도하십시오

IFS=$'\n'

1
$ IFS에 +1 그것은 구분 문자를 묘사합니다.
Ray

1
이것은 나를 위해 일한 솔루션입니다. comm정렬 된 파일 목록을 비교 하는 데 사용 하고 있었고 파일 이름에 공백이 있었지만 인용하지 않은 변수를 인용했지만. 그런 다음 cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html을 보았고 IFS = $ (echo -en "\ n \ b")로 $ IFS를 설정하는 솔루션이 효과적 이었습니다.
pbhj

우아하고 단순하며 아름다운 큰 따옴표 추가-감사합니다!
Big Rich


4

올바른 인용 외에도 findNULL 구분 기호를 사용하도록 지시 한 다음 while루프 에서 결과를 읽고 처리 할 수 ​​있습니다.

while read -rd $'\0' file; do
    something with "$file"
done < <(find  . -type f -name '*.*' -print0)

POSIX 호환 파일 이름을 처리해야합니다. man find

   -print0
          True; print the full file name on the standard output, followed by a null character (instead of the newline character that  -print  uses).   This  allows  file
          names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output.  This option corresponds to the
          -0 option of xargs.

이것은 나를 위해 일한 유일한 해결책입니다. 감사.
codefreak


1

공백이 포함 된 파일을 찾기 위해 이와 같은 작업을 수행했습니다.

IFS=$'\n'
for FILE in `/usr/bin/find $DST/shared -name *.nsf | grep -v bookmark.nsf | grep -v names.nsf`; do
    file $FILE | tee -a $LOG
done

매력처럼 일했습니다 :)



0

find의 -exec 옵션을 사용하는 것이 더 나을 수도 있습니다 .

find . -type f -name '*.*' -exec tar -cpf archive.tar {} +

그런 다음 찾기는 시스템 호출을 사용하여 명령을 실행하므로 공백과 줄 바꿈이 유지됩니다 (파이프 대신 특수 문자 인용이 필요함). "tar -c"는 아카이브가 이미 존재하는지 여부에 관계없이 작동하며 {} 또는 +도 따옴표로 묶을 필요가 없습니다.


-1

minerz029가 제안했듯이 find명령 의 확장을 인용해야합니다 . 또한 $FILE루프에서 모든 대체를 인용해야합니다 .

for FILE in "$(find . -type f  -name '*.*')"
do
    if [ ! -f archive.tar ]; then
        tar -cpf archive.tar "$FILE"
    else 
        tar -upf archive.tar "$FILE" 
    fi
done

점을 유의 $()구문 역 따옴표를 사용하는 것이 바람직한다; 이 U & L 질문을 참조하십시오 . POSIX이기 때문에 [[키워드를 제거하고 [명령으로 대체했습니다 .


[[[에 대해 [[더 새로운 것으로 보이며 globbing 및 정규식 일치와 같은 더 많은 기능을 지원합니다. [[단지에 bash, 아니지만sh
키리

@ minerz029 예. 그것이 내가 말하는 것입니다. 글 [[로빙 을 지원 한다는 것이 무슨 의미인지 모르겠습니다 . Greg의 위키 에 따르면 내부에서는 글 로빙이 발생하지 않습니다 [[.
Joseph R.

[ "ab" == a? ] && echo "true"다음 시도[[ "ab" == a? ]] && echo "true"
kiri

글쎄요. 이들은 정규식입니다 (느슨하게 해석 됨). 이것은 a*"이름이 시작하고 a이후에 임의의 수의 문자가있는 모든 파일"이 아니라 "다음에 임의의 수의 문자가 있음"을 의미 하기 때문에 의미 가 없습니다 . [ ab = a* ] && echo true 대 시도하십시오 [[ ab == a* ]] && echo true.
Joseph R.

아, [[그래도 정규 표현식은 [하지 않습니다. 혼란스러워해야 함
kiri
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.