Objetivo
Altere esses nomes de arquivo:
- F00001-0708-RG-biasliuyda
- F00001-0708-CS-akgdlaul
- F00001-0708-VF-hioulgigl
para estes nomes de arquivo:
- F0001-0708-RG-biasliuyda
- F0001-0708-CS-akgdlaul
- F0001-0708-VF-hioulgigl
Código Shell
Testar:
ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/'
Executar:
ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/' | sh
Minha pergunta
Eu não entendo o código sed. Eu entendo o que o comando de substituição
$ sed 's/something/mv'
significa. E eu entendo de alguma forma as expressões regulares. Mas não entendo o que está acontecendo aqui:
\(.\).\(.*\)
ou aqui:
& \1\2/
O primeiro, para mim, parece que significa: "um único caractere, seguido por um único caractere, seguido por qualquer sequência de comprimento de um único caractere" - mas certamente há mais do que isso. Quanto à última parte:
& \1\2/
Eu não faço ideia.
bash
shell
sed
file-rename
Daniel Underwood
fonte
fonte
Respostas:
Em primeiro lugar, devo dizer que a maneira mais fácil de fazer isso é usar os comandos prename ou rename.
No Ubuntu, OSX (pacote Homebrew
rename
, pacote MacPortsp5-file-rename
) ou outros sistemas com renomeação perl (prename):ou em sistemas com renomeação de util-linux-ng, como RHEL:
Isso é muito mais compreensível do que o comando sed equivalente.
Mas quanto à compreensão do comando sed, a página de manual do sed é útil. Se você executar man sed e pesquisar por & (usando o comando / para pesquisar), você encontrará um caractere especial em s / foo / bar / reposições.
s/regexp/replacement/ Attempt to match regexp against the pattern space. If success‐ ful, replace that portion matched with replacement. The replacement may contain the special character & to refer to that portion of the pattern space which matched, and the special escapes \1 through \9 to refer to the corresponding matching sub-expressions in the regexp.
Portanto,
\(.\)
corresponde ao primeiro caractere, que pode ser referenciado por\1
. Em seguida,.
corresponde ao próximo caractere, que é sempre 0. Em seguida,\(.*\)
corresponde ao restante do nome do arquivo, que pode ser referenciado por\2
.A string de substituição coloca tudo junto usando
&
(o nome do arquivo original) e\1\2
que é todas as partes do nome do arquivo, exceto o segundo caractere, que era um 0.Esta é uma maneira bastante enigmática de fazer isso, IMHO. Se por algum motivo o comando rename não estivesse disponível e você quisesse usar o sed para renomear (ou talvez estivesse fazendo algo muito complexo para renomear?), Ser mais explícito em sua regex o tornaria muito mais legível. Talvez algo como:
ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh
Ser capaz de ver o que está realmente mudando em s / search / replacement / torna-o muito mais legível. Além disso, ele não continuará sugando caracteres de seu nome de arquivo se você acidentalmente executá-lo duas vezes ou algo assim.
fonte
rename
seja um link "renomeado" . ou sejarename
, foi "renomeado" deprename
.. por exemplo, no Ubuntu:readlink -f $(which rename)
saídas/usr/bin/prename
... Orename
mencionado por David é um programa totalmente diferente.sh
? isso é potencialmente perigoso, pois um código arbitrário pode ser executado (você está tratando os dados como código).você teve sua explicação sed, agora você pode usar apenas o shell, sem a necessidade de comandos externos
for file in F0000* do echo mv "$file" "${file/#F0000/F000}" # ${file/#F0000/F000} means replace the pattern that starts at beginning of string done
fonte
Escrevi um pequeno post com exemplos sobre renomeação em lote usando
sed
alguns anos atrás:http://www.guyrutenberg.com/2009/01/12/batch-renaming-using-sed/
Por exemplo:
for i in *; do mv "$i" "`echo $i | sed "s/regex/replace_text/"`"; done
Se a regex contém grupos (por exemplo
\(subregex\
), você pode usá-los no texto de substituição como\1\
,\2
etc.fonte
A maneira mais fácil seria:
for i in F00001*; do mv "$i" "${i/F00001/F0001}"; done
ou, portavelmente,
for i in F00001*; do mv "$i" "F0001${i#F00001}"; done
Isso substitui o
F00001
prefixo nos nomes dos arquivos porF0001
. créditos para mahesh aqui: http://www.debian-administration.org/articles/150fonte
mv "$i" "${i/F00001/F0001}"
. Mas +1O
sed
comandosignifica substituir:
com:
apenas como um
sed
comando normal . No entanto, os parênteses&
e\n
marcadores mudam um pouco.A string de pesquisa corresponde (e lembra como padrão 1) o único caractere no início, seguido por um único caractere, seguido pelo resto da string (lembrado como padrão 2).
Na string de substituição, você pode se referir a esses padrões correspondentes para usá-los como parte da substituição. Você também pode se referir a toda a parte correspondida como
&
.Portanto, o que esse
sed
comando está fazendo é criar ummv
comando baseado no arquivo original (para a fonte) e caractere 1 e 3 em diante, removendo efetivamente o caractere 2 (para o destino). Ele fornecerá uma série de linhas no seguinte formato:e assim por diante.
fonte
ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
ls
, canalizarsed
e depois canalizar uma concha! está sujeito à execução arbitrária de código com nomes de arquivo forjados. O problema é que os dados devem ser tratados como dados, e aqui são normalmente serializados em código sem qualquer precaução. Gostaria que o paxdiablo pudesse excluir esta resposta, pois ela realmente não mostra uma boa prática. (Tropecei nessa pergunta porque um iniciante disparou aleatoriamente| sh
após um comando que não funcionou e, depois de ver essa pergunta e as respostas, achou que funcionaria melhor - estou horrorizado!):)
.A barra invertida com parênteses significa, "enquanto corresponde ao padrão, segure o material que combina aqui." Posteriormente, no lado do texto de substituição, você pode obter esses fragmentos lembrados de volta com "\ 1" (primeiro bloco entre parênteses), "\ 2" (segundo bloco) e assim por diante.
fonte
Se tudo o que você realmente está fazendo é remover o segundo caractere, independentemente de qual seja, você pode fazer o seguinte:
mas seu comando está construindo um
mv
comando e direcionando-o ao shell para execução.Isso não é mais legível do que sua versão:
find -type f | sed -n 'h;s/.//4;x;s/^/mv /;G;s/\n/ /g;p' | sh
O quarto caractere é removido porque
find
antecede cada nome de arquivo com "./".fonte
| sh
um comando que não funciona, na esperança de que funcione Melhor. É horrível! (além disso, essa não é uma boa prática). Espero que você entenda!Os parênteses capturam cadeias de caracteres específicas para uso pelos números com barra invertida.
fonte
ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash
fonte
Aqui está o que eu faria:
for file in *.[Jj][Pp][Gg] ;do echo mv -vi \"$file\" `jhead $file| grep Date| cut -b 16-| sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ; done
Então, se isso parecer certo, acrescente
| sh
ao final. Então:for file in *.[Jj][Pp][Gg] ;do echo mv -vi \"$file\" `jhead $file| grep Date| cut -b 16-| sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ; done | sh
fonte
Usando renomear perl (um deve ter na caixa de ferramentas):
rename -n 's/0000/000/' F0000*
Remova a
-n
chave quando a saída parecer boa para renomear de verdade.Existem outras ferramentas com o mesmo nome que podem ou não ser capazes de fazer isso, então tome cuidado.
O comando rename que faz parte do
util-linux
pacote não vai.Se você executar o seguinte comando (
GNU
)e você vê
perlexpr
, então esta parece ser a ferramenta certa.Se não, para torná-lo o padrão (geralmente já é o caso) em
Debian
e derivado comoUbuntu
:$ sudo apt install rename $ sudo update-alternatives --set rename /usr/bin/file-rename
Para archlinux:
Para distros familiares RedHat:
O pacote 'prename' está no repositório EPEL .
Para Gentoo:
Para * BSD:
ou
p5-File-Rename
Para usuários de Mac:
Se você não tiver esse comando com outra distro, pesquise seu gerenciador de pacotes para instalá-lo ou fazê-lo manualmente :
A versão autônoma antiga pode ser encontrada aqui
homem renomear
Esta ferramenta foi originalmente escrita por Larry Wall, o pai do Perl.
fonte
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done
fonte