다음과 같은 이름을 가진 파일이 많이 있습니다.
data1_1.txt
data1_2.txt
data1_3.txt
data2_1.txt
data2_2.txt
...
그러나 이들은 역순으로 다운로드되고 이름이 지정됩니다. 결과가 다음과 같도록 일괄적으로 이름을 바꾸려면 어떻게 해야 합니까?
data1_3.txt
data1_2.txt
data1_1.txt
data2_2.txt
data2_1.txt
...
내 첫 번째 아이디어는 단지 bash/zsh 스크립트이지만 더 잘 작동하는 다른 도구가 있으면 알려 주시기 바랍니다.
답변1
그리고 zsh
:
autoload zmv # best in ~/.zshrc
typeset -A c=()
zmv -n '(*)_<->.txt(#qnOn)' '$1_$((++c[${(b)1}])).txt-renamed' &&
: zmv '(*)-renamed' '$1'
( 원하는 경우 -n
(드라이런)을 제거하고 :
(드라이런 없이 다시 실행하기 전에 다시 초기화하는 것을 기억하십시오 c=()
) .
<->
<1-12>
: 십진수 범위 일치 와 유사 하지만 여기서는 경계가 지정되지 않으므로 하나 이상의 십진수 시퀀스가 일치됩니다.[0-9]##
where##
is 가zsh
ERE 와 동일 하다고 쓸 수도 있습니다+
.(#q...)
~이다분명히구문 지정글로벌 예선.n
: 숫자순으로 정렬On
: 이름을 기준으로 역순으로 정렬합니다. 따라서n
위의 방법을 사용하면 일치하는 파일 목록을 번호순으로 역정렬할 수 있습니다.- 교체를 위해서는 이전 부분인
$1
에서 캡쳐한 내용을 포함시킵니다 .(*)
_<digits>.txt
- 을 추가합니다
$((++c[${(b)1}]))
. 여기서 은$c
이전에 선언된 연관 배열입니다. ${(b)1}
이스케이프가 있는 전역 문자(이스케이프가 없으면$1
포함된 경우 제대로 작동하지 않습니다).$1
]
-renamed
프로세스에서 파일을 덮어쓰는 것을 방지하기 위해 두 단계(두 번째 단계에서 제거된 접미사 추가)로 이 작업을 수행합니다 .
샘플에서 다음을 제공합니다.
mv -- data2_2.txt data2_1.txt-renamed
mv -- data2_1.txt data2_2.txt-renamed
mv -- data1_3.txt data1_1.txt-renamed
mv -- data1_2.txt data1_2.txt-renamed
mv -- data1_1.txt data1_3.txt-renamed
mv -- data1_1.txt-renamed data1_1.txt
mv -- data1_2.txt-renamed data1_2.txt
mv -- data1_3.txt-renamed data1_3.txt
mv -- data2_1.txt-renamed data2_1.txt
mv -- data2_2.txt-renamed data2_2.txt
기술적으로는 그렇지 않습니다.역순으로또는 예와 같이 숫자가 1씩 증가하고 1에서 시작하는 경우에만 수행합니다. [1, 2, 3]
, [4, 5, 6]
, 모두 [0, 10, 20]
로 바뀔 것입니다 [3, 2, 1]
.
해당 목록을 뒤집으려면 조금 더 복잡합니다. 다음과 같이 보일 수 있습니다:
all_files=(*_<->.txt(n))
prefixes=(${all_files%_*})
for prefix (${(u)prefixes}) {
files=(${(M)all_files:#${prefix}_<->.txt})
new_files=(${(Oa)^files}-renamed)
for old new (${files:^new_files})
echo mv -i -- $old $new-renamed
}
(행복하면 삭제 echo
).
zmv '(*)-renamed' '$1'
두 번째 단계로 다시 실행합니다 .
[0, 3, 10, 20]
세 번째 예로 추가 목록이 있는 또 다른 예에서는 다음과 같습니다.
mv -i -- data1_1.txt data1_3.txt-renamed
mv -i -- data1_2.txt data1_2.txt-renamed
mv -i -- data1_3.txt data1_1.txt-renamed
mv -i -- data2_1.txt data2_2.txt-renamed
mv -i -- data2_2.txt data2_1.txt-renamed
mv -i -- data3_0.txt data3_20.txt-renamed
mv -i -- data3_3.txt data3_10.txt-renamed
mv -i -- data3_10.txt data3_3.txt-renamed
mv -i -- data3_20.txt data3_0.txt-renamed
이러한 솔루션은 파일 이름에 포함될 수 있는 문자(또는 문자가 아닌 문자)에 대해 가정하지 않으며 _<digits>.txt
. 기반 접근 방식으로 시작하지 않는 한 파일 이름을 바꾸지 않습니다. 후자 접근 방식과 달리 기존 접미사로 이름이 지정된 파일을 zmv
덮어쓰는 것을 방지합니다 ( 이 일이 발생하기 전에 메시지 가 표시 -renamed
되지만 ). 또는 접미사를 추가하는 대신 이름이 바뀐 파일을 디렉토리로 이동할 수 있습니다 .-i
mv
-renamed
renamed
답변2
다음은 파일 이름이 실제로 설명하는 방식( data<one-digit>_<digits>.txt
)이라고 가정하는 bash 스니펫입니다.
shopt -s extglob
#gather files into array
files=( data[[:digit:]]_+([[:digit:]]).txt )
#zip original files with their target file names and feed to mv
paste <(printf '%s\n' "${files[@]}" | sort -k1.5,1n -k2n -t'_') \
<(printf '%s.ren\n' "${files[@]}" | sort -k1.5,1n -k2nr -t'_') |
xargs -n 2 mv --
#strip the temporary .ren suffix
for f in data*.ren; do mv -- "$f" "${f%.ren}"; done
답변3
먼저 "old-"라는 접두사가 붙은 모든 파일의 이름을 바꿉니다.
for i in *
do
mv "$i" "old-$i"
done
그런 다음 이 명령을 실행하고 출력을 관찰하여 제대로 보이는지 확인합니다.
ls -v | tac | sort -s -t _ -k1,1 | sed -e 's/^old-//' | paste <(ls -v) - | sed -e 's/^/mv /'
그렇다면 출력을 sh로 파이프하십시오.
이것이 일어난 일입니다.
ls -v
정렬된 순서로 생성합니다. 예를 들어 -v는 9 다음에 11을 정렬한다는 의미입니다.- tac은 전체 입력을 반대로 합니다(전체 파일입니다. 인내심을 가지세요!)
- 정렬은 첫 번째 문자 이전의 문자만 안정적으로 정렬됨을 의미합니다
_
. 이것-k1,1
그리고-s
올바른 출력을 얻으려면 두 가지 모두 중요합니다. -k1,1이 없으면 줄의 나머지 부분은 우리가 원하지 않는 중복을 해결하는 데 사용되며, 중복이 없으면-s
순서는 임의적입니다.
나머지는 쉽습니다.
답변4
이것은 간단한 해결책이며 귀하의 상황에 완벽하게 작동할 것이라고 믿습니다. 여기서 하는 일은 먼저 접두사로 얻은 파일 형식 수를 확인하는 것입니다. 여기서 파일 유형 접두사는 data1_을 나타냅니다., 데이터2_등. 그런 다음 각 접두사 유형에 대해 사용 가능한 총 파일 수를 가져와 이름이 지정된 배열에 저장합니다 totalFilesForEachPrefix
.
그런 다음 1단계에서 파일 이름을 임시 파일로 바꿉니다. 확장명으로 이동하는 이유 otemp
는 이름 충돌을 방지하고 기존 파일을 덮어쓰는 것입니다. 여기서는 1_1 1_2 1_3 1_4 등의 파일이 있다고 가정합니다.
.otemp
그런 다음 2단계에서 확장 프로그램을 제거하겠습니다 .
#!/usr/bin/env bash
totalFileTypeByPrefix=$( for file in data*_1.txt; do echo $file; done | wc -l )
totalFilesForEachPrefix=()
for (( prefix = 1; prefix <= totalFileTypeByPrefix; prefix++ )); do
totalFilesForEachPrefix+=( $( for file in data${prefix}_*.txt; do echo $file; done | wc -l) )
done
### Step 1
prefix=1; type=0;
while (( prefix <= totalFileTypeByPrefix )); do
suffix=${totalFilesForEachPrefix[$type]}
for file in data${prefix}_*.txt; do
mv $file data${prefix}_${suffix}.txt.otemp; echo "$file renamed temporary --> data${prefix}_${suffix}.txt.otemp"
suffix=$((suffix -1))
done
type=$((type+1))
prefix=$((prefix+1))
done
### Step 2
echo "....Finally changing the temporary files"....
for tempfile in *.otemp; do
file=${tempfile::-6};
mv $tempfile $file; echo "$tempfile renamed final --> $file";
done
다음은 무슨 일이 일어나고 있는지에 대한 출력과 설명입니다.
data1_1.txt renamed temporary --> data1_3.txt.otemp
data1_2.txt renamed temporary --> data1_2.txt.otemp
data1_3.txt renamed temporary --> data1_1.txt.otemp
data2_1.txt renamed temporary --> data2_2.txt.otemp
data2_2.txt renamed temporary --> data2_1.txt.otemp
....Finally changing the temporary files....
data1_1.txt.otemp renamed final --> data1_1.txt
data1_2.txt.otemp renamed final --> data1_2.txt
data1_3.txt.otemp renamed final --> data1_3.txt
data2_1.txt.otemp renamed final --> data2_1.txt
data2_2.txt.otemp renamed final --> data2_2.txt