Por que os nomes das minhas pastas acabaram assim e como posso corrigir isso usando um script?

15

Desculpe se isso tiver uma resposta em outro lugar, não faço idéia de como procurar pelo meu problema.

Eu estava executando algumas simulações em um servidor HPC redhat linux, e meu código para lidar com a estrutura de pastas para salvar a saída teve um bug infeliz. Meu código matlab para criar a pasta era:

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

onde sp.run_numberestava um número inteiro. Esqueci de convertê-lo em uma string, mas por algum motivo a execução mkdir(folder);(no matlab) ainda teve êxito. De fato, as simulações foram executadas sem problemas e os dados foram salvos no diretório correspondente.

Agora, quando a estrutura da pasta é consultada / impressa, recebo as seguintes situações:

  • Quando tento tabular o preenchimento automático: run_ run_^A/ run_^B/ run_^C/ run_^D/ run_^E/ run_^F/ run_^G/ run_^H/ run_^I/
  • Quando eu uso ls: run_ run_? run_? run_? run_? run_? run_? run_? run_? run_? run_?.
  • Quando transfiro para o meu Mac usando o rsync, a --progressopção mostra: run_\#003/etc. com (presumo) o número que corresponde ao número inteiro sp.run_numberpreenchido com três dígitos, portanto a 10ª execução érun_\#010/
  • Quando visualizo as pastas no localizador, vejo run_ run_ run_ run_ run_ run_ run_ run_ run_ run_?
  • Olhando para esta pergunta e usando o comando ls | LC_ALL=C sed -n l, recebo:
run_$
run_\001$
run_\002$
run_\003$
run_\004$
run_\005$
run_\006$
run_\a$
run_\b$
run_\t$
run_$

Não consigo acessar cdas pastas usando nenhuma dessas representações.

Eu tenho milhares dessas pastas, então precisarei corrigir isso com um script. Qual dessas opções é a representação correta da pasta? Como posso referenciar programaticamente essas pastas para renomeá-las com um nome formatado corretamente usando um script bash? E acho que por uma questão de curiosidade, como diabos isso aconteceu em primeiro lugar?

Phill
fonte
4
"Quando tento tabular o preenchimento automático: ... Se eu tentar digitar ..." Por que digitar e não deixar o preenchimento automático completo para você? Também ^Anão é literalmente ^seguido por A, mas Ctrl-A (você pode digitar usando Ctrl-V Ctrl-A, pois Ctrl-A geralmente é um atalho para o shell).
Muru
@muru que não funciona ... Eu chegar o mais longe run_e eu tenho que digitar algo
Phill
Desculpe comentou antes que eu vi sua edição, que consegue me pegar na via cd
Phill
Possível duplicado da Select unicode filename em Bash
Muru
9
BTW, a "alguma razão" pela qual o mkdir no matlab fez isso é porque os ÚNICOS caracteres inválidos em um nome de arquivo ou diretório em sistemas de arquivos unix são NUL e barra invertida /. Qualquer outro caractere é válido, incluindo caracteres de controle. Não sei o que o matlab teria feito se sp.run_number fosse 0 (provavelmente abortar com um erro ou produzir run_, pois o byte NUL encerraria a string do nome do diretório). Obviamente, isso também seria problemático para valores de 16 bits (ou superiores) que continham um byte NUL e também variaria de acordo com a capacidade de endereçamento do sistema executando o matlab.
cas

Respostas:

26

Você pode usar o renameutilitário perl (aka prenameou file-rename) para renomear os diretórios.

NOTA: Isso não deve ser confundido com renamefrom util-linuxou com qualquer outra versão.

rename -n 's/([[:cntrl:]])/ord($1)/eg' run_*/

Isso usa a ord()função perl para substituir cada caractere de controle no nome do arquivo pelo número ordinal desse caractere. por exemplo, ^Atorna-se 1,^B torna-se 2, etc.

A -nopção é executar a seco para mostrar o rename que faria se você permitir. Remova-o (ou substitua-o por-v por saída detalhada) para renomear.

O emodificador na s/LHS/RHS/egoperação faz com que o perl execute o RHS (a substituição) como código perl e o$1 são os dados correspondentes (o caractere de controle) do LHS.

Se você quiser números preenchidos com zero nos nomes dos arquivos, poderá combinar ord()com sprintf(). por exemplo

$ rename -n 's/([[:cntrl:]])/sprintf("%02i",ord($1))/eg' run_*/ | sed -n l
rename(run_\001, run_01)$
rename(run_\002, run_02)$
rename(run_\003, run_03)$
rename(run_\004, run_04)$
rename(run_\005, run_05)$
rename(run_\006, run_06)$
rename(run_\a, run_07)$
rename(run_\b, run_08)$
rename(run_\t, run_09)$

Os exemplos acima funcionam se e somente se sp.run_number no seu script matlab no intervalo de 0 a 26 (portanto, foram produzidos caracteres de controle nos nomes dos diretórios).

Para lidar com QUALQUER caractere de 1 byte (ou seja, de 0 a 255), você usaria:

rename -n 's/run_(.)/sprintf("run_%03i",ord($1))/e' run_*/

Se sp.run_numberpudesse ser> 255, você teria que usar a unpack()função perl em vez de ord(). Eu não sei exatamente como o matlab gera um int não convertido em uma string, então você terá que experimentar. Vejoperldoc -f unpack para detalhes.

por exemplo, o seguinte descompactará os valores não assinados de 8 e 16 bits e os zerará com 5 dígitos de largura:

 rename -n 's/run_(.*)/sprintf("run_%05i",unpack("SC",$1))/e' run_*/
cas
fonte
Obrigado pelos detalhes! Eu estou tentando testá-lo com a -nopção, mas está me dizendo que é uma opção inválida - as informações da versão me fornecem, rename from util-linux 2.23.2então não tenho certeza se é a mesma função #
31519 Phill Phill
3
é por isso que eu especifiquei a versão perl do renameutilitário. util-linux's renameé muito diferente, muito menos capaz, e as opções de linha de comando são incompatíveis. Se você estiver executando o debian ou similar, tente instalar o file-renamepacote. caso contrário, instale o pacote apropriado para sua distribuição. já pode estar instalado, tente executar prenameou em file-renamevez de apenas rename.
cas
Sim, eu pensei que era esse o caso. Vou ver se consigo fazer um desses funcionar. Mais uma vez obrigado por dedicar um tempo para me ajudar!
26419 Phill
11

E eu acho que por uma questão de curiosidade, como diabos isso aconteceu em primeiro lugar?

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

onde sp.run_numberestava um número inteiro. Esqueci de convertê-lo em uma string, mas por algum motivo executando mkdir(folder); (no matlab) ainda conseguiu.

Portanto, parece que mkdir([...])no Matlab concatena os membros da matriz para criar o nome do arquivo como uma string. Mas você deu um número a ele, e os números são o que os caracteres de um computador realmente são. Então, quando sp.run_numberfoi 1, deu a você o personagem com valor 1, e depois o personagem com valor 2, etc.

Esses são caracteres de controle, eles não têm símbolos imprimíveis e imprimi-los em um terminal teria outras consequências. Então, em vez disso, eles geralmente são representados por diferentes tipos de escapadas: \001(octal), \x01(hex), ^Asão representações comuns para o personagem com valor1 . O caractere com valor zero é um pouco diferente, é o byte NUL usado para marcar o final de uma string em C e nas chamadas do sistema Unix.

Se você ultrapassasse 31, começaria a ver caracteres imprimíveis, 32 é espaço (embora não muito visível), 33 = !, 34 =" etc.

Então,

  • run_ run_^A/ run_^B/- O primeiro run_corresponde àquele com um byte zero, a string termina aí. Os outros mostram que seu shell gosta de usar exibir os códigos de controle com ^A. A notação também sugere que o caractere com valor numérico 1 pode ser inserido como Ctrl-A, embora você precise dizer ao shell para interpretar não como um caractere de controle, mas como um literal,Ctrl-V Ctrl-A deve fazer isso pelo menos no Bash.

  • sl: run_ run_? run_?- lsnão gosta de imprimir caracteres não imprimíveis no terminal, substitui-os por pontos de interrogação.

  • rsync: run_\#003/- isso é novo para mim, mas a idéia é a mesma, a barra invertida marca uma fuga e o restante é o valor numérico do personagem. Parece-me que o número aqui está em octal, como no mais comum \003.

  • usando o comando ls | LC_ALL=C sed -n l... run_\006$ run_\a$ run_\b$ run_\t$- \a, \be\t C escapam para alarme (campainha), backspace e tab, respectivamente. Eles têm os valores numéricos 7, 8 e 9, portanto, deve ficar claro por que eles vêm depois \006. Usar esses escapes C é mais uma maneira de marcar os caracteres de controle. Os cifrões à direita marcam o final da linha.

Quanto a cd, supondo que minhas suposições estejam corretas, cd run_deve-se ir para esse único diretório sem um caractere final ímpar e cd run_?deve dar um erro, pois o ponto de interrogação é um caractere glob que corresponde a qualquer caractere único e há vários nomes de arquivos correspondentes, mascd apenas espera um.

Qual dessas opções é a representação correta da pasta?

Todos eles, em certo sentido ...

No Bash, você pode usar as aspas \000e \x00escapes dentro das $'...'aspas para representar os caracteres especiais, então $'run_\033(octal) ou$'run_\x1b' corresponder ao diretório com o valor de caractere 27 (que passa a ser ESC). (Eu não acho que o Bash suporta escapes com números decimais.)

A resposta de cas tem um script para renomeá-los, então não vou lá.

ilkkachu
fonte
Se for GNU ls, existem algumas opções de citação, incluindo -b/ --escapee --quoting-style=, ou a QUOTING_STYLEvariável de ambiente, para controlar como os caracteres não imprimíveis são mostrados. Não acho que haja uma opção para fazê-lo preferir escapes octais às versões dos personagens.
Toby Speight
3

O mais fácil seria criar o nome do arquivo errado e o nome do arquivo correto no mesmo ambiente em que ocorreu o acidente e, em seguida, basta mover / renomear as pastas para os nomes corretos.

Para evitar colisões entre nomes existentes, use melhor outra pasta de destino.

./saveLocationA/wrongname1 -> ./saveLocationB/correctname1
./saveLocationA/wrongname2 -> ./saveLocationB/correctname2
./saveLocationA/wrongname3 -> ./saveLocationB/correctname3

Se possível, eu preferiria corrigir o script e apenas executá-lo novamente; corrigir alguns erros post mortem estranhos provavelmente custa mais e pode introduzir novos problemas.

Boa sorte!

Pedro
fonte