쉘 스크립트를 통해 수행하려는 프로젝트가 있습니다.
나는 약 30년 동안 지속되어 온 장기간의 주간 라디오 쇼 카탈로그를 가지고 있습니다. 출처가 다르기 때문에 이름 형식도 상당히 다를 수 있습니다. 이로 인해 내가 가지고 있는 쇼와 누락된 쇼가 무엇인지 알기가 어렵습니다.
표준 날짜 형식으로 심볼릭 링크를 만들고 파일 이름을 실제 디스플레이 디렉토리(있는 경우)에 대한 날짜 심볼릭 링크로 갖고 싶습니다.
예를 들어 내가 하고 싶은 말은
'2015-09-25' -> '../Radio Show/2015-09-25 Special Guest/'
'2015-10-02' -> '../Radio Show/Very funny! 2015-10-02 Show'
날짜 형식도 다양하지만 지금은 YY-MM-DD, YYYY-MM-DD 형식을 찾는 게 고민일 뿐입니다.
그래서 나는 다음을 사용하여 각 줄이 에서 1980-01-01
까지 의 날짜인 파일을 만들었습니다 .2010-12-31
이 답변.
그런 다음 각 줄을 읽고 이를 사용하여 find
이름에 해당 문자열이 있는 디렉터리를 찾습니다. 그러나 find
30년 전의 모든 날짜에 대해 전체 디렉터리 트리에서 작업을 수행하려면 시간이 오래 걸립니다*.
그래서 저는 find -type d . > filesystem.txt
모든 디렉터리 이름을 포함하는 파일을 만들곤 했습니다. 그런 다음 grep
각 날짜 문자열에 대해 디스크에서 실행하는 대신 해당 파일의 각 날짜 문자열 에 대해 실행할 수 있습니다 find
.
그러나 날짜 파일의 각 줄을 grep으로 로드하는 데 문제가 있습니다.
But을 사용하면 $ grep -f dates.txt filesystem.txt
다음 형식으로 모든 결과를 얻을 수 있습니다.
./complete/1996-02-18
./complete/1996-03-03
./complete/1996-03-31
...
문자열 매개변수를 사용하여 결과를 얻는 방법을 모르겠습니다.
'1996-03-31' -> './complete/1996-03-31'
나는 이것을 시도했지만 $ grep "${dates[@]}" metadata/filesystem.txt
예상한 대로 작동하지 않습니다.
grep: 1988-01-03: No such file or directory
grep: 1988-01-04: No such file or directory
내가 하려는 일의 의사코드 버전은 다음과 같습니다.
foreach ( date-string in dates.txt ) {
grep date-string in filesystem.txt
if (match) {
ln -s match date-string
}
}
Bash에서 어떻게 할 수 있나요?
-* 모든 날짜를 사용하지 않음으로써 이를 단순화할 수 있지만, 라디오 쇼가 모든 기록에 대해 같은 날에 진행되는지는 확실하지 않습니다. 날짜를 놓치지 않았는지 확인하고 싶기 때문에 30년 범위 내의 모든 날짜를 사용하고 싶습니다.
답변1
주제의 질문에 답하십시오.grep을 사용하여 배열의 요소를 찾는 방법.
a=(foo bar baz)
grep "${a[@]}" files
할 것이다:
grep foo bar baz files
즉, 검색 foo
하거나 원하는 것이 아닙니다 bar
.baz
files
당신이 원하는 것:
grep 'foo
bar
baz' files
대신에. 이렇게 하려면 다음을 수행합니다.
IFS=$'\n'
grep -- "${a[*]}" files
구문을 사용할 때 첫 번째 문자 $IFS
는 배열의 요소를 연결하는 데 사용됩니다 "${a[*]}"
. 이는 배열을 지원하는 모든 쉘에서 작동합니다( ksh
, zsh
, bash
, yash
(해당 $'\n'
부분은 아직 작동하지 않지만 yash
리터럴 개행 문자를 사용해야 함)).
를 사용하면 zsh
다음 작업도 수행할 수 있습니다.
grep -e$^a files
이는 다음으로 확장됩니다.
grep -efoo -ebar -ebaz files
이는 다른 문자열을 검색하는 또 다른 방법입니다.
(배열에 일치시킬 정규식 대신 검색할 고정 문자열이 포함되어 있는 경우 이 옵션을 사용해야 합니다 -F
.)
답변2
그리고 zsh
:
autoload zmv # best in ~/.zshrc
zmv -Ls -n '../Radio Show/(^*[0-9])((19|)(<80-99>~^??)|(20|)(<0-16>~^??))(-<1-12>-<1-31>~^-??-??)(^[0-9]*)' '${4:+19$4}${6:+20$6}$7'
-n
건식 실행에 사용됩니다. 제안된 작업이 만족스러우면 제거하여 실제로 링크를 만드세요.
zmv
파일 충돌이나 덮어쓰기를 방지하는 역할을 담당합니다. 구체적인 zsh
전역 연산자는 다음과 같습니다.
<1-12>
1에서 12 사이의 10진수 정수로 확인되는 문자열과 일치합니다. 2012년의 012와 일치합니다.^x
: 부정x~y
(and-not): y와 일치하지 않는 한 x와 일치하는 문자열입니다. 따라서<1-12>~^??
1부터 12까지의 두 자리 숫자와 일치합니다(01과 일치하지만 1이나 0001은 일치하지 않음).(x|y)
: ERE와 같이 대체됩니다.
누락된 19 또는 20 날짜를 YY-MM-DD 형식으로 삽입합니다.
답변3
John1024의 답변이 아마도 최고일 것입니다. 그러나 완전성을 위해 의사 코드 구현은 다음과 같습니다.
for datestring in $(cat dates.txt)
do if match="$(grep "$datestring" filesystem.txt)"
then echo ln -s "$match" "$datestring"
fi
done
in을 남겨두었 echo
기 때문에 제거하기 전까지는 아무 작업도 수행되지 않습니다. 그러나 위의 내용은 모든 날짜를 매개변수로 확장해야 하므로 다음을 선호해야 합니다.
while read datestring
do if match="$(grep "$datestring" filesystem.txt)"
then echo ln -s "$match" "$datestring"
fi
done <dates.txt
$datestring
공백이 없다는 것을 알면서도 큰따옴표를 추가하여 아무 것도 변경하지 않도록 했습니다.
답변4
내가 올바르게 이해했다면 다음과 같은 filesystem.txt 파일이 있습니다.
$ cat filesystem.txt
../Radio Show/Very funny! 2015-10-02 Show
../Radio Show/2015-09-25 Special Guest/
생각해 보세요:
$ sed -E 's/.*[^[:digit:]]([[:digit:]]{2,4}-[[:digit:]]{2}-[[:digit:]]{2}).*/ln -s "&" "\1"/' filesystem.txt >script
위의 명령은 일련의 명령 처럼 보이는 명령을 생성합니다 script
. script
bash
$ cat script
ln -s "../Radio Show/Very funny! 2015-10-02 Show" "2015-10-02"
ln -s "../Radio Show/2015-09-25 Special Guest/" "2015-09-25"
이 파일을 확인하고 원하는 경우 실행하십시오.
bash script