저는 스크립팅(bash 쉘)을 처음 접했고 제가 수행하려는 이 작업의 논리를 이해할 수 없습니다.
예를 들어 다음과 같은 파일 이름을 가진 폴더가 있습니다.
PIC-100
PIC-1
PIC-23
PIC-5
파일 이름에서 번호를 가져와 가장 낮은 것부터 높은 것까지 순위를 매긴 다음 순위 목록에서의 위치에 따라 이름을 바꿀 수 있기를 원합니다.
예를 들어, 이름은 다음과 같이 변경됩니다.
PIC-100 --> PIC-4
PIC-1 --> PIC-1
PIC-23 --> PIC-3
PIC-5 --> PIC-2
다들 감사 해요.
답변1
이 문제를 해결하는 방법을 단계별로 보여 드리겠습니다. 먼저 몇 가지 테스트 데이터를 만들어 보겠습니다.
mkdir /tmp/blah && cd /tmp/blah
for f in 1 90 $RANDOM $RANDOM $RANDOM; do touch PIC-$f; done
ls
파일은 이름순으로 정렬되어야 하지만 우리는 숫자순으로 정렬되기를 원합니다. 이 명령은 sort
이 문제를 해결하는 데 도움이 될 수 있습니다.
ls | sort -V
-V
귀하의 경우에 적합한 "버전별 주문"을 의미합니다. 다른 옵션에 대해서는 을 참조하십시오 man sort
.
이제 이름을 바꾸겠습니다. 이를 수행하는 방법은 여러 가지가 있지만 정렬된 목록의 출력을 반복하겠습니다. 변수와 루프가 필요합니다.
counter=1 ls PIC-* | sort -V | while read fname; do
echo mv -i "$fname" NEWPIC-$counter
let counter+=1
done
여기에는 많은 일이 일어나고 있으므로 안전부터 자세히 설명하겠습니다. 첫 번째,파일 백업계속하기 전에 루핑은 쉽게 잘못되어 나쁜 하루를 초래할 수 있습니다. 모든 것을 백업하는 명령은 다음과 같습니다.
mkdir backup; cp PIC* backup
이 루프에 문제가 발생하면 엉망이 될 수 있으므로, 루프를 실행하기 전에 어떤 일이 일어나는지 확인하고 싶었습니다. 알아채다 echo
? 그러면 예상되는 명령을 실행하는 대신 인쇄됩니다. 출력을 삭제 echo
하거나 복사하여 붙여넣어 "실제로" 명령을 실행할 수 있습니다.
다음으로 나는 mv -i
이름 변경을 위해 싸우기 시작했습니다. 내가 무언가를 취재한다면 이것은 암시할 것입니다. 그때쯤이면 모든 것이 어쨌든 배 모양이 되었을 것이지만 적어도 나는 알 것입니다.
셋째, 이름 PIC-xxx
을 NEWPIC-xxx
. 이것은정말 중요한어떤 경우에는 실수로 PIC-3의 이름을 PIC-2로 바꿀 수도 있기 때문입니다(예를 들어 어떤 이유로 PIC-2.5가 존재하는 경우). NEW
이 섹션은 나중에 정리할 수 있습니다 .
또한 start 명령을 사용합니다 counter=1 ls | sort -V | ...
. 변수를 설정하여존재하다테스트 실행 사이에 설정하는 것을 잊어버렸을 때 "이상한" 동작이 표시되는 것을 방지하기 위한 명령 - 대상 파일 이름은 NEWPIC-6
다음과 같이 시작 됩니다 NEWPIC-1
. 이는 몇 번 실수를 한 후 얻게 되는 습관 중 하나입니다.
이제 루프 자체에 대해 이야기해 보겠습니다.
while read fname
"입력 줄을 볼 때마다 해당 줄을 변수에 넣고fname
다음 부분을 실행하세요.do .. done
각 줄에 대해 실행하려는 코드를 둘러싸세요.echo mv -i "$fname" NEWPIC-$counter
실행하려는 명령을 인쇄하십시오.let counter+=1
카운터 변수를 늘리십시오. 이것을 잊어버리면 결국 하나의 파일을 호출하게 되고NEWPIC-1
나머지 파일은 모두 사라집니다. 그래서 우리는 백업을 가지고 있습니다 :)
NEW
마지막으로 파일 이름의 시작 부분에서 이를 제거 하는 약간의 트릭은 다음과 같습니다 .
for fname in NEWPIC-*; do echo mv $fname ${fname#NEW}; done
echo
이전과 마찬가지로 출력이 만족스러우면 삭제하세요.
답변2
이 주석이 달린 쉘 스크립트는 원하는 작업을 수행합니다. 세미콜론은 명령의 순차적 실행을 나타내고, 수직 막대는 파이프라인을 나타내며, 파운드 기호는 주석을 시작하고, "do-done" 쌍은 "while"에 포함된 루프를 나타냅니다.
n=1 ; # initialize counter
ls | # list current working directory
sort -V | # sort (thanks to dwurf for the flag, but this isn't in POSIX standard yet)
while read f ; do
mv "$f" PIC-$n ; # rename file
n=$((n+1)) ; # counter increment.
done
답변3
zsh
쉘 사용 :
tmpdir=$(mktemp -d)
n=1
for name in PIC-*(.n); do
mv $name $tmpdir/PIC-$(( n++ ))
done
mv $tmpdir/PIC-* .
rmdir $tmpdir
그러면 현재 디렉터리에서 이름이 일치하는 모든 일반 파일을 PIC-*
임시 디렉터리로 이동하고 이름을 바꿉니다. 파일은 파일 이름의 번호를 기준으로 정렬된 숫자 순서로 이동됩니다(이것은 glob 한정자가 n
수행하는 작업입니다 (.n)
. .
일반 파일만 선택됩니다). 각 파일에 대해 카운터가 있는 PIC-n
곳에서 파일 이름이 변경됩니다 .n
모든 파일을 이동하고 이름을 바꾼 후에는 현재 디렉터리로 다시 이동되고 임시 디렉터리는 삭제됩니다.
이름 충돌을 방지하려면 임시 디렉터리를 준비 영역으로 사용하세요.
수천 개의 파일이 있는 경우 mv
"인수 목록이 너무 김" 오류를 방지하기 위해 루프에서 두 번째 파일을 실행해야 할 수도 있습니다.
# first loop as before, then...
for name in $tmpdir/PIC-*; do
mv $name .
done
rmdir $tmpdir
cp
끝에 파일 대신 파일이 있는 경우 "역순"으로 수행하면 임시 디렉터리에 파일의 원래 이름이 백업됩니다.mv
tmpdir=$(mktemp -d)
mv PIC-*(.) $tmpdir
n=1
for name in $tmpdir/PIC-*(n); do
# change "mv" to "cp" (and remove "rmdir" below)
# to leave a copy in the temporary directory
mv $name PIC-$(( n++ ))
done
rmdir $tmpdir