Considerar:
$ ls
foo xyfooz.tex
$ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
$ ls ./foo*
xyfooz.tex
Estou tentando obter a mesma sequência de instruções apenas com mv, não com find:
$ ls
foo xyfooz.tex
$ mv *foo* foo
mv: cannot stat '*foo*': No such file or directory
$ ls ./foo*
xyfooz.tex
Além disso (editar),
$ 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'
Como garantir que o aviso 'stat' não apareça. Em outras palavras, eu acho que eu refinaria o padrão para limitar a fonte a arquivos e não a diretórios?
GNU bash, versão 4.3.48(1)-versão (x86_64-pc-linux-gnu)
$ cat /etc/os-release
NAME="Linux Mint"
VERSION="18.3 (Sylvia)"
Responder1
Commv
cannot move 'foo' to a subdirectory of itself
é inofensivo neste caso, você pode ignorá-lo. Quero dizer, mv
moveremos o resto apesar do aviso. Porém o status de saída não será 0
, isso pode ser um grande obstáculo em scripts onde você deseja abortar ou executar uma ação de correção caso mv
não tenha sucesso.
Isso já foidisse em um comentário: você pode silenciar mv
redirecionando stderr com 2>/dev/null
; outros avisos ou erros também serão redirecionados.
Acho que você não pode facilmente "refinar o padrão para limitar a fonte a arquivos e não a diretórios". Ainda assim, você pode adotar uma abordagem que não corresponda ao literal foo
. A abordagem a seguir não tem nada a ver com arquivos ou diretórios; apenas com nomes, porque os globos lidam com nomes; portanto, pode não ser suficiente em alguns casos.
O *foo*
glob corresponde a quatro tipos disjuntivos de objetos:
foo
em si- foo apenas com prefixo, como
bar-foo
- você pode combiná-los por*?foo
- foo com prefixo e postfix, como
bar-foo-baz
–*?foo?*
- foo apenas com postfix, como
foo-baz
–foo?*
Para excluir foo
você precisa apenas dos três últimos (2-4), então a primeira abordagem pode ser:
mv *?foo *?foo?* foo?* foo/
A menos que você tenha a nullglob
opção shell definida, qualquer padrão precisa corresponder a alguma coisa, caso contrário, será passado mv
literalmente. Você não quer isso. A solução é executar o seguinte comando previamente:
shopt -s nullglob
Esta opção de shell fará com que qualquer globo incomparável se expanda para nada. Aconselho você a pesquisar dotglob
a opção também.
Observe que *?foo*
corresponde a (2-3). Jogos semelhantes *foo?*
(3-4). Além disso, considere --
informar mv
que todos os argumentos a seguir não são opções. Desta forma, um arquivo como --foo
não será interpretado como uma opção (desconhecida). Isso leva a dois comandos melhores:
mv -- *?foo* foo?* foo/
ou
mv -- *?foo *foo?* foo/
Não importa qual você escolha. Apenas não use mv *?foo* *foo?* foo/
; ele passará (por exemplo) bar-foo-baz
para mv
duas vezes (ou seja, como dois argumentos separados), gerando assim um erro quando a ferramenta tentar mover este objeto pela segunda vez.
Quando nenhuma correspondência for encontrada, meus mv
comandos se degenerarão mv foo/
e gerarão um erro. Seu mv
comando original (com nullglob
unset) obteria o literal *foo*
e geraria outro erro.
Exemplo de sessão de console:
$ 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'
$
Hack simples
Digamos dummy
que não existe.
mv foo dummy
mv *foo* dummy/
mv dummy foo
A renomeação de um diretório deve ser muito rápida em sistemas de arquivos baseados em inode. Programas que possuem arquivos foo/
já abertos não devem interferir nem quebrar porque é o inode que importa. No entanto, se algum programa precisar abrir um arquivo (incluindo seu caminho foo/
) entre o primeiro e o terceiro mv
, ele falhará. Outros efeitos colaterais podem ocorrer (imagine um programa de terceiros recriando foo/
enquanto isso), é por isso que chamo essa abordagem de hack. Pense duas vezes antes de usá-lo.
Com find
qualquer maneira
Separar arquivos de diretórios é uma tarefa para find
. O que você fez find
foi basicamente o caminho certo. O que você quer fazer (e o que eu fiz acima) com mv
shell globbing parece... bem, "menos certo", no máximo. Este é o seu comando:
find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
Isso find
executará um arquivo separado mv
para cada arquivo correspondente; todo o comando terá um desempenho ruim. Por esse motivo, pode-se querer mudar para um único arquivo mv
. Se esta é a sua linha de pensamento (e você mv
é find
rico o suficiente), então você deve considerar esta maneira "muito correta":
find . -maxdepth 1 -type f -name '*foo*' -exec mv -t foo/ -- {} +
As vantagens sobre o seu find
comando e/ou simples mv
com glob(s):
- um único
mv
pode ter vários arquivos para trabalhar; - ainda assim, se houver muitos arquivos para serem manipulados por uma linha de comando,
find
serão executadosmv
processos adicionais; - no caso em que nenhum arquivo corresponda ao padrão,
mv
não será executado; - graças a
--
um arquivo como--foo
não será interpretado como uma opção (desconhecida) paramv
;