숫자 순서를 반대로하여 파일 이름 바꾸기

숫자 순서를 반대로하여 파일 이름 바꾸기

다음과 같은 이름을 가진 파일이 많이 있습니다.

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 가 zshERE 와 동일 하다고 쓸 수도 있습니다 +.
  • (#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되지만 ). 또는 접미사를 추가하는 대신 이름이 바뀐 파일을 디렉토리로 이동할 수 있습니다 .-imv-renamedrenamed

답변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

관련 정보