나는 이것에 익숙하지 않으며 이 쉘 스크립트를 작성하는 방법을 알고 싶습니다.
디렉터리 1에 파일 이름을 가진 파일이 있고 파일 이름의 두 번째 부분을 기준으로 A1-001.xyz A29-002.xyz A82-003.xyz
이 파일을 디렉터리 2로 이동하고 싶습니다 . 폴더 이름은 001 002 003입니다.001 002 003
지금까지 내가 한 일은 다음과 같습니다.
for file in /path/to/directory1/** ; do
echo "$file" | awk -F '[-]' '{print $2}' | cut -f 1 -d '.' ;
done >> dummy.txt
input="dummy.txt"
while IFS= read -r file; do
echo "$file" | mv "$file" /path/to/directory2/$file ;
done
내 생각은 출력 파일 이름의 첫 번째 부분을 dummy.txt에 넣은 다음 파일 이름을 읽고 이동하는 것입니다. 스크립트의 두 번째 부분이 작동하지 않는 것 같습니다. 이를 수행하는 방법에 대한 제안 사항이 있습니까?
답변1
작은 것부터
문제를 더 작은 부분으로 나누십시오. 막히는 이유 중 하나는 솔루션 자체를 구축하는 데 사용되는 도구를 작동하는 방법을 배우려고 노력하면서도 전체 솔루션을 한 번에 구축하려고 하기 때문입니다.
여기에 문제 해결에 도움이 되는 팁이 있고, 나중에 비슷한 문제를 분석하고 분석해야 할 때 여러분과 다른 초보 스크립터가 도움이 될 수 있는 팁이 있기를 바랍니다.
각 파일에 대해 수행해야 하는 작업의 정확한 특성을 지정하는 것부터 시작하세요. 실제로 파일 목록에서 샘플링된 하나의 특정 파일 이름을 처리하는 데 필요한 명령을 수동으로 작성할 수 있어야 합니다. 작동하지 않고 명령만 작성합니다.귀하의 예에서는 모든 파일을 이동해야 합니다. 그렇죠? 따라서 각 파일에는 mv
명령이 필요합니다. 어떻게 해야 할지 고민하기보다는하다명령 mv
을 생성하는 방법만 걱정하면 됩니다. 수동으로 어떻게 작성하겠습니까?하나mv
파일을 이동하는 명령은 무엇입니까 ? 그러면 질문은 awk
이 명령을 어떻게(또는 사용하려는 도구) 출력하는지가 됩니다.
mv (filename) (to-where-you-want-it)
지정한 각 파일 이름에 대해. 새로운 도구를 배우면 실제로 수행하지 않고도 일련의 셸 명령을 출력으로 생성하는 스크립트를 디버깅하는 것이 더 쉽습니다.행위옆으로 가고 수백 개의 잘못된 파일을 수백 개의 잘못된 디렉토리로 옮기는 스크립트를 디버깅하는 것보다 더 중요한 것은 없습니다. 이제 더 이상 어떤 것이 어디에 있는지 확신할 수 없습니다.
우선, man
귀하에게 적합하다고 생각되는 도구에 대한 페이지를 확인하십시오. 그런 다음 수동 모드에서 명령을 실험하여 도구가 입력을 원하는 방식으로 구문 분석하고 필요한 출력을 생성하도록 하기 위해 수행해야 할 작업에 대한 아이디어를 얻으십시오. 100개 또는 1000개의 파일을 이동하는 스크립트를 작성하기 전에 해당 파일을 올바르게 이동하는 스크립트가 필요합니다.단 하나문서. 따라서 테스트 사례를 만들고 효과가 있다고 생각되는 도구를 사용하여 "친구를 사귀는" 시간을 보내십시오. 귀하의 게시물이 태그되었습니다앗현명한 선택이라고 생각하니 해보자.
awk
문자열을 구성요소 필드로 나누는 데 사용되는 구분 기호를 -F
지정하는 데 사용할 수 있는 매개변수가 있습니다 . awk
구분 기호는 간단한 문자일 수도 있고 괄호로 묶인 여러 문자일 수도 있습니다. 정규식 용어로 이것은 다음과 같습니다.캐릭터 클래스. 입력에서는 하이픈 '-'
과 마침표를 모두 필드 구분 기호로 사용하므로 하이픈이나 마침표로 분할 하도록 지시하는 '.'
문자 클래스를 지정할 수 있습니다 . 어느 것이 무엇인지는 신경쓰지 말고 소스 디렉터리에 하이픈이나 마침표가 포함되어 있지 않은지 주의 깊게 확인하세요 .[-.]
awk
awk
awk
각 파일 이름을 구성 요소 필드로 나누는 데 사용됩니다 .
파일 이름의 예를 가져오고 이 명령을 사용하여 수동으로 실행하여 파일 이름이 수행하는 A1-001.xyz
작업 을 awk
확인하세요 .awk
$ awk -F[-.] '{print $0 " " $1 " " $2 " " $3}' <<< 'A1-001.xyz'
명령은 다음과 같이 말합니다 awk
. "하이픈과 마침표를 필드 구분 기호로 사용하여 전체 입력 줄( $0
), 공백, 필드 1, 공백, 필드 2, 공백, 마지막으로 필드 3을 인쇄합니다.
출력은 다음과 같습니다
A1-001.xyz A1 001 xyz
이것이 여러분에게 많은 것을 보여주기를 바랍니다. 이것은 명령의 소스에서 필요한 것 $0
입니다 . 왜냐하면 그것이 전체 원본 파일 이름이기 때문입니다. 이것은 여러분이 원하는 숫자 디렉터리 이름이기 때문에 명령의 대상에서 필요한 것입니다 . 가장 큰 구현은 완전히 형식을 지정 하고 인쇄할 수 있는 명령입니다. 필요한 것은 약간 수정된 진술뿐입니다. 스크립트가 모든 것을 하게 하려고 하기보다는 스크립트가 모든 것을 하게 하세요.mv
$2
mv
awk
mv
awk
print
만들다실행해야 하는 명령입니다. 이렇게 하면 스크립트의 오류로 인해 스크립트가 충돌하고 파일이 잘못된 위치로 이동되지 않습니다. 일부 잘못된 출력을 인쇄하면 그것이 잘못되었음을 알 수 있지만 해를 끼치지는 않습니다.
awk
정제 명령의 두 번째 반복
파일 이름 앞에는 소스 경로가 올 수 있습니다. 하지만 .
경로에 또는 문자가 없는지 확인하세요 ! -
따라서 mv
각 파일에 대한 명령은 mv
공백으로 시작한 다음 파일 이름(전체 소스 경로를 포함할 수 있음), 다른 공백 및 파일을 이동할 디렉터리로 시작합니다. 좋은 측정을 위해 대상 디렉터리 뒤에 슬래시를 추가하겠습니다. 당신이 이래로아니요파일 이름을 변경하려면 대상 디렉터리를 지정하고 대상 파일 이름을 생략하면 됩니다. 또한 수행하기가 더 쉽기 때문에 주목할 가치가 있습니다. 필요 이상으로 일을 더 어렵게 만들지 마십시오.
$ awk -F[-.] '{print "mv " $0 " " $2 "/"}' <<< '/path/to/directory1/A1-001.xyz'
mv /path/to/directory1/A1-001.xyz 001/
명령을 살펴보세요 print
. 공백으로 시작한 mv
다음 $0
전체 파일 이름이 공백으로 표시됩니다. 이는 $2
출력 하위 디렉터리입니다. 다시 한번 소스 경로 이름을 확인해야 합니다.원하지 않는다파일 이름에서 필드 구분 기호로 특별한 의미를 갖기 때문에 하이픈이나 마침표를 포함하십시오. 더 많은 문제가 있습니다. awk
필드가 올바르게 분할되지 않아 스크립트가 중단됩니다.
그러나 대상 디렉토리는 단지 가 아니라 $2
소스 파일 이름과 마찬가지로 앞에 접두사가 있습니다. awk
매번 동일하므로 인쇄 할 수 있습니다 .
$ awk -F[-.] '{print "mv " $0 " /path/to/directory2/" $2 "/"}' <<< '/path/to/directory1/A1-001.xyz'
mv /path/to/directory1/A1-001.xyz /path/to/directory2/001/
전체 파일 목록에서 솔루션 테스트
그래서 이것은 유망해 보입니다. 이제 파일 목록을 만듭니다 file-list.txt
.
$ cat file-list.txt
A1-001.xyz
A29-002.xyz
A82-003.xyz
그런 다음 awk
전체 파일 목록에 대해 명령을 실행합니다. 여기서는 나쁜 일이 전혀 없다는 것을 기억하세요. awk
완료된 모든 작업은 다음 과 같습니다.인쇄물건. 실제로 파일 이동에 대해서는 아무 것도 수행하지 않습니다. 단지 당신이 하고 싶은 일을 수행하는 명령을 보여줄 뿐입니다.
$ awk -F[-.] '{print "mv " $0 " /path/to/directory2/" $2 "/"}' < file-list.txt
mv A1-001.xyz /path/to/directory2/001/
mv A29-002.xyz /path/to/directory2/002/
mv A82-003.xyz /path/to/directory2/003/
출력을 다시 확인하고 테스트하고 실행하십시오.
이동할 파일이 많은 경우 awk
위 명령을 파이프로 연결하여 less
다시 확인할 수 있어야 합니다. 잘못된 위치에 점과 대시가 있는지, 파일이나 디렉터리 이름에 다른 이상한 문자가 있는지 찾아보세요. 원하는 경우 이 출력의 샘플 라인을 복사하여 쉘 프롬프트에 붙여넣어 제대로 작동하는지 확인할 수 있습니다. 그러나 이는 검사를 통해 테스트할 수 있을 만큼 충분히 간단한 예입니다. 이 mv
명령 목록이 원하는 작업이라는 확신이 들면 awk
출력을 직접 파이프하여 sh
실행하면 됩니다. 실행되는 명령을 보려면 sh -v
다음 대신 사용하십시오 sh
.
$ awk -F[-.] '{print "mv " $0 " /path/to/directory2/" $2 "/"}' < file-list.txt | sh -v
mv A1-001.xyz /path/to/directory2/001/
mv A29-002.xyz /path/to/directory2/002/
mv A82-003.xyz /path/to/directory2/003/
$
결론적으로
이렇게 상세한 분석에 반대하지 않으셨으면 좋겠습니다. 하지만 이와 같은 질문은 Stack Exchange에 자주 등장하며, 많은 초보 스크립터는 자신의 문제가 고유한 솔루션이 필요한 고유한 일회성 문제라고 생각합니다.
스크립팅의 진정한 핵심은 스크립팅이 다양한 문제를 해결할 수 있는 일반적인 도구를 제공한다는 점을 깨닫는 것입니다. 숙련을 얻기 위한 첫 번째 단계는 이러한 도구를 사용하여 작은 작업을 수행하는 방법을 배우고 그 작은 작업을 점점 더 큰 작업으로 결합하는 것입니다. 것들.
awk
첫 번째 단계는 파일 이름을 필요한 방식으로 분리하는 방법을 배우는 것입니다 . 이는 여러 정보가 포함된 파일 이름에서 구성 요소 필드를 구문 분석하려고 할 때마다 중요한 단계입니다.
두 번째 단계는 awk에게 각 파일에 대해 항상 동일한 명령 부분( 필드 앞의 시작 mv
, 대상 경로)을 자동으로 인쇄하고 추출된 파일 이름 필드를 올바른 위치에 배치하도록 지시하는 것입니다. 명령문과 그 유사물은 모든 유형의 코딩에서 가장 기본적인 부분 중 하나이며, 적절한 명령문 으로 인해 많은 피해가 발생했다는 것을 기억할 수 없습니다. 확실히 필요한 것만 출력하고 싶지만, 학습하면서 변수가 무엇인지 모른다면 인쇄해 보세요. 물어보는 것도 나쁘지 않습니다. 장기적으로는 해당 인쇄 문을 제거하게 될 것이지만 스크립팅의 "인쇄 후 쉘로 파이프" 기술의 요점은 "테스트 실행"이 내장되어 있다는 것입니다. 왜냐하면 항상 무슨 일이 일어나는지 살펴보기 때문입니다. 실제로 스크립트를 셸에 파이프하기 전에 실행하기 전에 스크립트의 셸 명령 출력을 확인하세요. 복잡한 경우에는 "작업을 표시"하기 위해 출력에 설명을 추가하는 것도 공정한 게임입니다.$2
print
print
$ awk -F[-.] '{print "# move file " $0 " to subdir " $2; print "mv " $0 " /path/to/directory2/" $2 "/"}' < file-list.txt
# move file A1-001.xyz to subdir 001
mv A1-001.xyz /path/to/directory2/001/
# move file A29-002.xyz to subdir 002
mv A29-002.xyz /path/to/directory2/002/
# move file A82-003.xyz to subdir 003
mv A82-003.xyz /path/to/directory2/003/
세 번째 핵심은 제 두 번째 요점과 밀접한 관련이 있을 수 있지만 종종 간과되는 것 중 하나는 약간 어려운 일을 하고 있을 때 잘못될 수 있는 스크립트를 작성하지 말고 그대로 두는 것입니다. 파일은 셀 수 없이 다양하지만 잘못된 위치에 흩어져 있습니다. 그냥 스크립트를 작성하세요스크립트작업을 수행합니다. 이렇게 하면 문제 해결이 훨씬 쉽습니다. 그런 다음 마침내 올바른 스크립트를 얻었을 때 스크립트 출력(예에서는 mv
각 파일에 대해 하나씩 일련의 명령)을 셸에 파이프하면 실행됩니다.
답변2
스크립트의 두 번째 부분은 두 가지 문제로 인해 실패합니다. 첫째, 실제로 루프에 대한 입력을 읽지 않습니다. 넌 해본 적 있니:
while IFS= read -r file; do something; done
하지만 다음이 필요합니다.
while IFS= read -r file; do something; done < "$inputFile"
그러면 mv
입력 스트림에서 데이터를 읽을 수 없으며 데이터를 입력 스트림으로 전송할 필요가 없습니다. 소요된다파일 이름텍스트가 아닌 입력으로 사용되며 어쨌든 표준 입력에서는 읽지 않습니다. 그러니 echo "$file" | mv "$file" "/somewhere"
지금 달리는 것과 똑같습니다 mv "$file" "/somewhere"
. 이것은 echo $file
의미가 없습니다. 실제 파일 이름이 아닌 파일 이름의 두 번째 부분( etc. ) $file
만 있기 때문에 작동하지 않습니다 .001
002
어쨌든 중간 파일이 필요 없이 루프를 사용하여 모든 작업을 직접 수행할 수 있습니다.
for file in /path/to/directory1/** ; do
dirName=$(awk -F[-.] '{print $2}' <<<"$file");
echo mv "$file" "/path/to/directory2/$dirName";
done
필요한 내용이 인쇄되면 삭제 echo
하고 다시 실행하여 실제로 파일을 이동하세요.