Renomeando o script bash do diretório - destino… Nenhum arquivo ou diretório

1

Novo no bash scripting, e só precisa de um pouco de ajuda. Eu tenho testado isso em uma estrutura de diretórios simples.
Estou tentando alterar qualquer diretório e / ou subdiretório com o nome "Season" para "Sn". Cheguei a um ponto em que o roteiro mudaria o que eu queria ... exceto pelo diretório principal, como visto na lista abaixo - "Season 1" "Season 2" "Season 3".

Estrutura de diretórios:

.
├── AnotherShow
│   ├── Sn1
│   ├── Sn2
│   ├── Sn3
│   └── Sn4
├── Movie1
│   ├── Sn1
│   └── Sn2
├── Movie2
│   ├── Sn1
│   ├── Sn2
│   ├── Sn3
│   └── Sn4
├── Movie3
│   ├── Sn1
│   └── Sn3
├── Movie4
│   ├── Sn2
│   └── Sn3
├── Season 1
├── Season 2
├── Season 3
├── Show
│   ├── Sn1
│   ├── Sn2
│   ├── Sn3
│   ├── Sn4
│   └── Sn5
└── TV
    ├── Sn1
    ├── Sn2
    ├── Sn3
    └── Sn4

Meu script é:

array="$(find . -maxdepth 2 -type d -iname 'Season*' -print)"; # A more refined way to search for Seasons in    a directory.
for dir in "${array[@]}"; do # Put this list into an array.  Surround the array with quotes to keep all space   s (if any) together.
   new="$(echo "$dir" | sed -e 's/Season 0/Sn/' -e 's/Season /Sn/')"; # Only change Season 0 to SN.  Leave othe   rs alone.
   sudo mv -v "$dir" "$new" && echo Changed "$dir" to "$new"; 
done                                                           
    
por Happyworker 04.11.2014 / 01:27

3 respostas

2

O problema é que você não está criando uma matriz, você está criando uma string. Você pode testar isso facilmente, tentando imprimir o primeiro elemento da sua matriz:

$ array="$(find . -maxdepth 2 -type d -iname 'Season*' -print)"; 
$ echo $array
./Season 3 ./Season 1 ./Season 2

Parece bom né? Mas se isso fosse uma matriz, você seria capaz de imprimir cada elemento separadamente. Infelizmente, o acima é uma string simples 1 e não uma matriz:

$ echo ${array[0]}
./Season 3 ./Season 1 ./Season 2
$ echo ${array[1]}

A matriz na verdade consiste em uma única string, e é por isso que o segundo elemento da "matriz" ( ${array[1]} ) está vazio. Em qualquer caso, você não precisa de um array para isso, basta ler a saída de find (você também não precisa do -print , é o que faz por padrão). Uma versão funcional do seu script pode ser:

#!/usr/bin/env bash
find . -maxdepth 2 -type d -iname 'Season*' | sort -r |
while IFS= read -r dir
do 
    mv "$dir" ${dir/Season /Sn} && echo Changed "$dir" to "$dir/Season /Sn}"; 
done

Existem alguns truques usados aqui. Primeiro, o loop while para percorrer os resultados de find . Esse é o mesmo princípio básico que o loop for que você usou, mas combinado com read pode ler da entrada padrão.

O IFS= é necessário para evitar a divisão no espaço em branco (sem ele, o diretório Season 1 seria dividido em Season e 1 ).

O -r de read diz para não permitir que as barras invertidas escapem de caracteres (coisas como \t e \n são tratadas literalmente), o que garante que isso possa lidar até com os nomes mais estranhos.

O sort -r é necessário para que, se você tiver um diretório correspondente ao padrão dentro de outro diretório correspondente, o filho seja listado antes do pai. Por exemplo:

./Season 12/Season 13
./Season 12

Isso garante que você execute mv "Season 12" "Sn12" após executando mv "Season 12"/"Season 13" "Season 12"/"Sn13" . Se você não fizer isso, o segundo comando falhará, pois Season 12 não existe mais.

Por fim, removi o sudo . Você deve executar o script como sudo script.sh . Nunca é uma boa ideia ter aleatórias sudo chamadas em seus scripts.

1 Não tenho certeza sobre os detalhes, mas, aparentemente, o bash trata variáveis de string como matrizes de um elemento.

    
por 04.11.2014 / 02:48
0

Ainda não sei qual é o problema, mas tenho algumas observações sobre o seu script.

Tente não chamar binários (com um único dado) em um loop. Isso mata o desempenho.

Você deve chamar todo o script com sudo mas não sudo dentro do script.

Você não precisa de sed para alterar os nomes dos arquivos. bash pode fazer isso sozinho:

if [[ $dir =~ "Season 0" ]]; then
  new="${dir/Season 0/Sn}"
else
  new="${dir/Season /Sn}"
fi

Ou sem o if :

new="${dir/Season /Sn}"
new="${new/Sn0/Sn}"

Você não precisa da matriz:

find ... | while read dirpath; do

Isso tem a "vantagem" de que os espaços no nome do diretório não quebram a definição da matriz. Não tenho ideia do porque seu array="$(find ... -print)" funciona. Não deve (porque o shell não deve fazer diferença na divisão de palavras entre espaço e nova linha).

    
por 04.11.2014 / 02:16
0

Apenas uma variante curta acima

for dir in ./*/Season\ */ 
do
  mv "$dir" "${dir/eason /n}" && echo "$dir changed to ${dir/eason /n}"
done

Funciona com subdiretórios Temporada . Se você quiser renomear os arquivos (não apenas dirs), basta remover / no final do padrão para que pareça ./*/Season\ *

Se você precisar de sudo , eu lhe ofereceria para fazer isso em um comando de uma linha (não em um script)

for dir in ./*/Season\ */ ; do sudo mv -v "$dir" "${dir/eason /n}" ; done
    
por 04.11.2014 / 15:11