
고려하다:
$ ls
foo xyfooz.tex
$ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
$ ls ./foo*
xyfooz.tex
find가 아닌 mv로만 동일한 명령 시퀀스를 달성하려고합니다.
$ ls
foo xyfooz.tex
$ mv *foo* foo
mv: cannot stat '*foo*': No such file or directory
$ ls ./foo*
xyfooz.tex
추가적으로 (수정),
$ ls -F
foo/ xyfooz.tex*
$ mv -v *foo* foo
mv: cannot move 'foo' to a subdirectory of itself, 'foo/foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
'stat' 경고가 나타나지 않는지 확인하는 방법. 즉, 소스를 디렉터리가 아닌 파일로 제한하도록 패턴을 개선해야 할까요?
GNU bash, 4.3.48(1)-릴리스(x86_64-pc-linux-gnu)
$ cat /etc/os-release
NAME="Linux Mint"
VERSION="18.3 (Sylvia)"
답변1
와 함께mv
cannot move 'foo' to a subdirectory of itself
이 경우에는 무해하므로 무시해도 됩니다. mv
경고에도 불구하고 나머지는 옮기겠다는 뜻입니다 . 그러나 종료 상태는 가 아니며 , 이는 중단하거나 성공하지 못한 0
경우 수정 조치를 취하려는 스크립트에서 큰 장애물이 될 수 있습니다 .mv
이것은 이미댓글에서 말했다mv
: ; 로 stderr을 리디렉션하여 침묵시킬 수 있습니다 2>/dev/null
. 하지만 다른 경고나 오류도 리디렉션됩니다.
나는 "소스를 디렉토리가 아닌 파일로 제한하도록 패턴을 수정"하는 것이 쉽지 않다고 생각합니다. 그래도 리터럴과 일치하지 않는 접근 방식을 취할 수 있습니다 foo
. 다음 접근 방식은 파일이나 디렉터리와 관련이 없습니다. 글로브는 이름을 다루기 때문에 이름만 사용합니다. 그래서 어떤 경우에는 충분하지 않을 수도 있습니다.
glob *foo*
은 네 가지 분리형 객체 유형과 일치합니다.
foo
그 자체- 접두사만 있는 foo
bar-foo
– 다음과 같이 일치시킬 수 있습니다.*?foo
- 접두사와 접미사가 있는 foo, 예
bar-foo-baz
: –*?foo?*
- 접미사만 있는 foo, 예
foo-baz
: –foo?*
제외하려면 foo
후자의 세 개(2-4)만 필요하므로 첫 번째 접근 방식은 다음과 같습니다.
mv *?foo *?foo?* foo?* foo/
쉘 옵션을 설정 하지 않은 한 nullglob
, 모든 패턴은 무엇인가와 일치해야 합니다. 그렇지 않으면 문자 그대로 전달됩니다 mv
. 당신은 이것을 원하지 않습니다. 해결 방법은 다음 명령을 미리 실행하는 것입니다.
shopt -s nullglob
이 셸 옵션은 일치하지 않는 모든 글로브를 아무것도 확장하지 않게 만듭니다. 나는 당신에게 dotglob
옵션을 연구하는 것이 좋습니다.
*?foo*
(2-3) 과 일치합니다. 마찬가지로 *foo?*
(3-4)와 일치합니다. 또한 다음 인수는 모두 옵션이 아니라는 점을 --
고려 하세요. mv
이렇게 하면 다음과 같은 파일이 --foo
(알 수 없는) 옵션으로 해석되지 않습니다. 이로 인해 두 가지 더 나은 명령이 생성됩니다.
mv -- *?foo* foo?* foo/
또는
mv -- *?foo *foo?* foo/
어느 것을 선택하든 상관없습니다. 그냥 사용하지 마세요 mv *?foo* *foo?* foo/
; 이는 (예를 들어) 두 번(즉, 두 개의 별도 인수로) 전달되므로 bar-foo-baz
도구 mv
가 이 개체를 두 번째로 이동하려고 시도할 때 오류가 생성됩니다.
일치하는 항목이 전혀 없으면 mv
명령이 자체적으로 저하되어 mv foo/
오류가 발생합니다. 원래 mv
명령( nullglob
설정되지 않음)은 리터럴을 가져오고 *foo*
또 다른 오류를 발생시킵니다.
콘솔 세션 예:
$ shopt -s nullglob
$ mkdir foo
$ touch bar-foo bar-foo-baz foo-baz xyfooz.tex ./--foo
$ mv -v -- *?foo* foo?* foo/
'bar-foo' -> 'foo/bar-foo'
'bar-foo-baz' -> 'foo/bar-foo-baz'
'--foo' -> 'foo/--foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
'foo-baz' -> 'foo/foo-baz'
$
간단한 해킹
dummy
존재하지 않는다고 가정 해 봅시다 .
mv foo dummy
mv *foo* dummy/
mv dummy foo
inode 기반 파일 시스템에서는 디렉토리 이름 바꾸기가 매우 빠릅니다. 이미 열려 있는 파일이 있는 프로그램은 foo/
중요한 inode이기 때문에 방해하거나 중단되어서는 안 됩니다. 그러나 프로그램이 foo/
첫 번째와 세 번째 사이에 파일을 ( 포함 경로로) 열어야 하는 경우 mv
실패합니다. 다른 부작용이 발생할 수도 있습니다(그 동안 타사 프로그램이 다시 생성된다고 상상해 보세요 foo/
). 이것이 바로 제가 이 접근 방식을 해킹이라고 부르는 이유입니다. 사용하기 전에 두 번 생각해보세요.
어쨌든find
파일과 디렉토리를 구분하는 것은 find
. 당신이 한 일은 find
기본적으로 올바른 방법입니다. 당신이 하고 싶은 일(그리고 내가 위에서 한 일)과 mv
쉘 글로빙은… 글쎄, 기껏해야 "덜 옳은" 것 같습니다. 이것은 당신의 명령입니다:
find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
find
일치하는 모든 파일에 대해 별도로 실행되며 전체 mv
명령이 제대로 수행되지 않습니다. 이러한 이유로 단일 mv
. 이것이 당신의 사고 방식이고 당신의 mv
과 당신 find
이 충분히 부유하다면 다음과 같은 "매우 올바른" 방법을 고려해야 합니다.
find . -maxdepth 1 -type f -name '*foo*' -exec mv -t foo/ -- {} +
find
명령 및/또는 일반 glob에 비해 장점은 mv
다음과 같습니다.
- 하나의 파일
mv
로 작업할 수 있는 파일이 여러 개 있을 수 있습니다. - 그러나 하나의 명령줄에서 처리할 파일이 너무 많으면 추가 프로세스를
find
실행합니다 .mv
- 패턴과 일치하는 파일이 없으면
mv
전혀 실행되지 않습니다. --
같은 파일 덕분에 에 대한--foo
(알 수 없는) 옵션으로 해석되지 않습니다mv
.