디렉토리를 포착하는 패턴과 일치하는 파일 이동

디렉토리를 포착하는 패턴과 일치하는 파일 이동

고려하다:

$ 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*은 네 가지 분리형 객체 유형과 일치합니다.

  1. foo그 자체
  2. 접두사만 있는 foo bar-foo– 다음과 같이 일치시킬 수 있습니다.*?foo
  3. 접두사와 접미사가 있는 foo, 예 bar-foo-baz: –*?foo?*
  4. 접미사만 있는 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.

관련 정보