디렉토리의 파일을 반복하고 경로를 변경하고 파일 이름에 접미사를 추가하는 방법


562

다른 인수로 프로그램을 시작하는 스크립트를 작성해야하지만 Bash를 처음 사용합니다. 다음과 같이 프로그램을 시작합니다.

./MyProgram.exe Data/data1.txt [Logs/data1_Log.txt].

내가하고 싶은 것에 대한 의사 코드는 다음과 같습니다.

for each filename in /Data do
  for int i = 0, i = 3, i++
    ./MyProgram.exe Data/filename.txt Logs/filename_Log{i}.txt
  end for
end for

그래서 첫 번째 인수에서 두 번째 인수를 만드는 방법에 대해 의아해합니다. 그래서 dataABCD_Log1.txt처럼 보이고 프로그램을 시작합니다.

답변:


736

먼저 몇 가지 참고 사항 : Data/data1.txt인수로 사용할 때 실제로 /Data/data1.txt슬래시가 있어야 합니까? 또한 외부 루프는 .txt 파일 또는 / Data의 모든 파일 만 검색해야합니까? 다음은 /Data/data1.txt.txt 파일 만 가정 하고 이에 대한 답변입니다 .

#!/bin/bash
for filename in /Data/*.txt; do
    for ((i=0; i<=3; i++)); do
        ./MyProgram.exe "$filename" "Logs/$(basename "$filename" .txt)_Log$i.txt"
    done
done

노트:

  • /Data/*.txt(/ 데이터의 텍스트 파일의 경로로 확장 포함 은 / 데이터 / 부분)
  • $( ... ) 쉘 명령을 실행하고 해당 지점에서 출력을 명령 행에 삽입합니다.
  • basename somepath .txt.txt를 끝에서 제거하여 somepath의 기본 부분을 출력합니다 (예 : /Data/file.txt-> file)

당신이 MyProgram을 실행하는 데 필요한 경우 Data/file.txt대신 /Data/file.txt, 사용은 "${filename#/}"선도적 인 슬래시를 제거합니다. 반면에 실제로 스캔 Data하지 /Data않으려면을 사용하십시오 for filename in Data/*.txt.


1
분명히 basename -s비표준 확장입니다. 표준 구문을 사용하도록 답변을 편집하겠습니다.
Gordon Davisson

21
파일을 찾을 수 없거나 와일드 카드와 일치하지 않는 경우 for 루프 실행 블록을 찾으면 filename = "/Data/*.txt"로 한 번만 입력됩니다. 어떻게 피할 수 있습니까?
Oliver Pearmain

20
@OliverPearmain shopt -s nullglob루프 전에 ( shopt -u nullglob나중에 문제를 피하기 위해) 사용하거나 if [[ ! -e "$filename ]]; then continue; fi루프의 시작 부분에 추가 하면 존재하지 않는 파일은 건너 뜁니다.
Gordon Davisson

9
이름에 공백이 포함 된 파일이 있으면 작동하지 않습니다.
이사 Hassen

4
@Isa 모든 큰 따옴표가있는 한, 공백으로 작동해야합니다. 큰 따옴표를 남겨두면 공백에 문제가 있습니다.
Gordon Davisson

344

스레드를 괴롭히는 것은 유감 스럽지만, globbing하여 파일을 반복 할 때마다 glob가 일치하지 않는 코너 케이스를 피하는 것이 좋습니다 (루프 변수가 (일치하지 않는) glob 패턴 문자열 자체로 확장됩니다).

예를 들면 다음과 같습니다.

for filename in Data/*.txt; do
    [ -e "$filename" ] || continue
    # ... rest of the loop body
done

참조 : 배쉬 함정


8
이것은 여전히시기 적절한 경고입니다. 스크립트를 잘못 작성했다고 생각했지만 대문자 대신 파일 확장자가 소문자이며 파일을 찾지 못하고 glob 패턴을 반환했습니다. 우와
RufusVS

5
Bash 태그가 사용되었으므로 이것이 shopt nullglob바로 그 일 입니다! (또는 shopt failglob원하는 동작에 따라 사용할 수도 있습니다).
gniourf_gniourf

또한 루프가 파일에 도달하기 전에 처리 중에 삭제 된 파일도 검사합니다.
loxaxs

1
dir.txt
vidstige

75
for file in Data/*.txt
do
    for ((i = 0; i < 3; i++))
    do
        name=${file##*/}
        base=${name%.txt}
        ./MyProgram.exe "$file" Logs/"${base}_Log$i.txt"
    done
done

name=${file##*/}대체 ( 쉘 매개 변수 확장은 ) 마지막까지 선두 경로 이름을 제거합니다 /.

base=${name%.txt}대체 후행를 제거합니다 .txt. 확장 기능이 다를 수 있다면 조금 까다 롭습니다.


3
코드에 오류가 있다고 생각합니다. 한 줄은 base=${name%.txt}대신에 이어야합니다 base=${base%.txt}.
caseklim

4
@CaseyKlimkowsky : 예; 코드와 주석이 일치하지 않으면 그 중 하나 이상이 잘못되었습니다. 이 경우, 나는 그것이 유일한 코드라고 생각합니다. 종종 실제로는 둘 다 잘못된 것입니다. 지적 해 주셔서 감사합니다. 나는 그것을 고쳤다.
Jonathan Leffler 1

8

디렉토리 구조를 안전하게 반복하기 위해 read와 함께 null로 구분 된 출력 옵션을 사용할 수 있습니다.

#!/bin/bash
find . -type f -print0 | while IFS= read -r -d $'\0' file; 
  do echo "$file" ;
done

그래서 당신의 경우를 위해

#!/bin/bash
find . -maxdepth 1 -type f  -print0 | while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done

추가적으로

#!/bin/bash
while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done < <(find . -maxdepth 1 -type f  -print0)

스크립트 (process)의 현재 범위에서 while 루프를 실행하고 필요한 경우 find 출력을 변수 설정에 사용할 수 있습니다.


2
$'\0'이상한 쓰기 방법입니다 ''. 누락 IFS=되었으며 다음으로 -r전환 read하십시오. read 문은 다음과 같아야합니다 IFS= read -rd '' file.
gniourf_gniourf

일부는 검색이 필요 $'\0'하고 일부 스택 지점을 확산 시킬 것이라고 생각합니다 . 지적한 내용을 수정하려고합니다. IFS=시도 하지 않은 나쁜 결과는없는 echo -e "ok \nok\0" | while read -d '' line; do echo -e "$line"; done것 같습니다. 또한 -r 나는 종종 기본값이지만, 그것이 방지되는 것에 대한 예를 찾지 못했습니다.
제임스

4
IFS=파일 이름이 공백으로 끝나는 경우 필요합니다 touch 'Prospero '. 또한 -r파일 이름에 백 슬래시가있는 경우 스위치 가 필요합니다 .로 시도하십시오 touch 'Prospero\n'.
gniourf_gniourf

-1

Windows 파일 (.exe)을 실행하려는 것 같습니다. 확실히 powershell을 사용해야합니다. 어쨌든 리눅스 bash 쉘에서 간단한 one-liner로 충분합니다.

[/home/$] for filename in /Data/*.txt; do for i in {0..3}; do ./MyProgam.exe  Data/filenameLogs/$filename_log$i.txt; done done

또는 bash에서

#!/bin/bash

for filename in /Data/*.txt; 
   do
     for i in {0..3}; 
       do ./MyProgam.exe Data/filename.txt Logs/$filename_log$i.txt; 
     done 
 done
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.