콘텐츠 패턴 일치를 기반으로 파일을 디렉터리로 이동합니다.

콘텐츠 패턴 일치를 기반으로 파일을 디렉터리로 이동합니다.

files특정 콘텐츠가 포함된 기존 디렉터리의 파일을 호출하는 스크립트를 작성하여 기존 또는 새 디렉터리 및 하위 디렉터리 로 이동하고 해당 파일을 dir/subdir로 이동하고 싶습니다 .fruit~/bin

files예를 들어, file1 file2 file3.... file100이라는 기존 디렉터리에 많은 일반 파일이 있습니다 .

ls:

file1
file2 
file3 
file4 
... 
file100

파일 내용은 다음과 같습니다.

cat file1

apple 1
789098

cat file2

orange 2
389342

cat file3

pear 1
678034

cat file4

grapes 3
123432

cat file5

apple 3
342534

cat file6

apple 3
234298

field1의 첫 번째 줄이 동일한 파일을 field1과 동일한 이름을 가진 새 디렉터리로 이동하고 파일 이름은 변경하지 않고 싶습니다.

  • file1, file5, file6로 이동apple
  • file2가다orange
  • file3가다pear
  • 등.

ls:

apple pear grapes orange etc ...

./apple:
file1 file5 file6

./pear:
file3

/orange:
file2

그런 다음 새 하위 디렉터리를 만들고 field2 콘텐츠의 첫 번째 줄이 동일한 파일을 해당 하위 디렉터리로 이동하려고 합니다.

  • 디렉토리 아래에서 apple하위 file1디렉토리로 이동하고 1, file5하위 디렉토리로 이동하고 3, file6하위 디렉토리로 이동합니다.3
  • 디렉토리 아래에서 하위 디렉토리로 이동합니다 orange.file22
  • 디렉토리 아래에서 하위 디렉토리로 이동합니다 pear.file31
  • 등.

정렬 및 이동 후 파일은 다음과 같이 정렬되어야 합니다.

ls:

apple pear grapes orange etc ...

./apple:
1 3

./apple/1:
file1

./apple/3:
file5 file6

./orange:
2

./orange/2:
file2

./pear
1

./pear/1
file3

vi 편집기를 사용하여 모든 파일을 반복하고 이를 쉘의 해당 디렉터리 및 하위 디렉터리로 이동하려면 어떻게 해야 합니까?

답변1

$ find . -name 'file*' 
./file6
./file1
./file5
./file2
./file3
./file4

$ perl -lane '
    close(ARGV);
    mkdir $F[0] unless -e $F[0];
    mkdir "$F[0]/$F[1]" unless -e "$F[0]/$F[1]";
    rename $ARGV, "$F[0]/$F[1]/$ARGV" if (-d "$F[0]/$F[1]");
  ' file*

$ find . -name 'file*' 
./pear/1/file3
./grapes/3/file4
./orange/2/file2
./apple/1/file1
./apple/3/file5
./apple/3/file6

file{1..6}샘플 파일입니다. Perl 스크립트는 각 파일을 차례로 열고 첫 번째 줄을 읽고 이를 배열로 분할합니다 @F(Perl의 -a명령줄 옵션을 통해). 그런 다음 파일을 닫고(라인 카운터를 재설정하는 부작용이 있음 $.) 디렉토리를 생성하고(아직 존재하지 않는 경우) 파일을 디렉토리로 이동합니다(실제로 디렉토리인 경우). 이미 존재하는 경우 일반 파일, 심볼릭 링크 또는 디렉터리가 아닌 다른 것일 수 있습니다.

한 줄 이상 없는 파일은 무시됩니다. 예상과 다른 형식의 파일(예: 첫 번째 줄에는 기본 디렉터리 이름과 하위 디렉터리 이름을 포함하여 모든 종류의 공백으로 구분된 두 개의 필드가 포함되어 있음)은 정의되지 않은(아마도 이상하거나 재앙적일 수 있는) 결과를 초래할 수 있습니다.

이 두 find명령은 Perl one-liner를 실행하기 전후에 파일 위치를 표시하는 데 사용됩니다. 이는 출력을 생성하지 않는 최소한의 스크립트입니다. 또한 오류 검사나 데이터 유효성 검사도 충분하지 않습니다.


독립 실행형 스크립트로서의 대체 버전입니다. 이 글을 쓰는 유일한 진짜 이유는
Perl 옵션에 대한 Stéphane의 의견을 다루기 위한 것입니다 -T(대부분의 경우 문제가 되지 않습니다... 하지만 사람들은 파일 이름을 사용하여 병적으로 미친 짓을 하고 심지어 악의적인 일도 하므로 조심하세요. /잘못된 것은 없습니다.) 편집증이 있음):

$ cat sort-move.pl 
#!/usr/bin/perl

use strict;
use File::Path qw(make_path);

while(<<>>) {
  my($dir,$subdir) = split;
  close(ARGV);
  make_path("$dir/$subdir");
  rename $ARGV, "$dir/$subdir/$ARGV" if (-d "$dir/$subdir");
}

예를 들어 실행해 보세요 ./sort-move.pl file*. 디렉터리 생성 오류가 이제 치명적인 조건이라는 점을 제외하면 결과는 한 줄 버전과 정확히 동일합니다.

추가 오류 검사나 데이터 유효성 검사는 수행하지 않습니다. 실제로는 더 적은 작업을 수행합니다( 디렉토리를 생성하기 위해 make_path()핵심 Perl 모듈 File::Path의 기능 에 의존합니다. make_path()매우 유사하게 작동합니다 mkdir -p). 즉, 나쁜 데이터는 여전히 나쁜 일을 하게 만들 것이므로 나쁜 데이터를 제공하지 마십시오.

그러나 이미 존재하고 디렉토리가 아니어서(또는 파일 시스템 공간 부족이나 inode 부족과 같은 다른 이유로 인한 오류로 인해) 실패하는 경우 make_path이제 스크립트는 오류 메시지와 함께 즉시 종료됩니다. "$dir/$subdir"예를 들어, mkdir apple; touch apple/1이전에 이 스크립트를 실행했다면 오류 메시지는 다음과 같을 것입니다 mkdir apple/1: File exists at ./sort-move.pl line 9.. 스크립트는 디렉터리를 생성하지 않으며 파일을 이동하지 않습니다. 나는 그것을 테스트하기 위해 정확히 그랬기 때문에 이것을 알고 있습니다.

전체 스크립트는 오류 조건을 적절하게 처리합니다. 전체 스크립트에는 내용만 표시하는 -n또는 옵션도 있습니다.--dry-run회의실제로 하지 않고도 그냥 해보세요. 이것은 완전한 스크립트는 아니지만가장 작은원하는 것을 수행하는 한 가지 방법의 실제 예입니다.

답변2

zsh쉘 사용 ( vi귀하의 요청에 따라 대부분의 쉘과 같은 줄 편집 모드가 있지만 이것이 어떻게 관련되는지는 알 수 없습니다):

typeset -A files=()

for file (file*(N.L+3))
  read -r dir subdir ignore < $file &&
    [[    $dir != (|.|..|*/*) ]] &&
    [[ $subdir != (|.|..|*/*) ]] &&
    files[$dir/$subdir]+=$file$'\0'

if (($#files))
  mkdir -p -- ${(k)files} &&
    for dir (${(k)files}) mv -i -- ${(0)files[$dir]} $dir/

files위는 A키가 대상 디렉토리인 연관 배열이며, 일치하는 각 파일의 첫 번째 줄의 처음 두 개의 IFS 구분 필드로 구성됩니다 file*(N.L+3)(이름은 file일반 ( .)으로 시작하고 L3보다 깁니다( x y\n크기 4는 한 줄입니다). 두 개의 필드를 포함하는 파일)).

보호 조치로 ...디렉토리 구성 요소나 포함을 허용하지 않습니다 /.

연관 배열 요소의 값은 지정된 대상 디렉토리에 있는 NUL로 구분된 파일 목록입니다.

그런 다음 이러한 모든 디렉터리를 한 번에 생성하고 성공한 후에만 해당 디렉터리에서 파일 이동을 시작합니다.

답변3

나는 이것을 하고 싶습니다(Bash 또는 POSIX-y 쉘에서):

for f in ./*; do
    read -r a b < "$f"
    mkdir -p -- "$a/$b"
    mv -- "$f" "$a/$b"
done

즉, 파일( read한 줄)을 반복하고 두 개의 필드로 분할하고 해당 필드를 기반으로 디렉터리를 만든 다음 파일을 해당 디렉터리로 이동합니다. 필요에 따라 상위 디렉터리를 -p생성 mkdir하고 기존 디렉터리를 무시하는 옵션입니다.

예, 각 파일에 대해 한 번씩 호출되므로 추가 작업이 수행됩니다. 예, mkdir파일 에 .mv/dev/foo

예제 파일에서 이를 실행하면 다음이 제공됩니다.

$ ls -R
.:
apple/  grapes/  orange/  pear/

./apple:
1/  3/

./apple/1:
file1

./apple/3:
file5  file6

./grapes:
3/
[...]

관련 정보