Planear o diretório, mas preservar nomes de diretório no novo nome de arquivo

9

Como posso achatar um diretório no seguinte formato?

Antes: ./aaa/bbb/ccc.png

Depois: ./aaa-bbb-ccc.png

    
por Nathan J. Brauer 16.08.2012 / 18:51

6 respostas

7

Aviso: eu digitei a maioria desses comandos diretamente no meu navegador. Leitor de advertências.

Com zsh e zmv :

zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'

Explicação: O padrão **/* corresponde a todos os arquivos nos subdiretórios do diretório atual, recursivamente (ele não corresponde aos arquivos no diretório atual, mas eles não precisam ser renomeados). Os dois primeiros pares de parênteses são grupos que podem ser referenciados como $1 e $2 no texto de substituição. O par final de parênteses adiciona o D qualificador de glob para que os arquivos de pontos não sejam omitido. -o -i significa passar a opção -i para mv , para que você seja questionado se um arquivo existente seria sobrescrito.

Com apenas ferramentas POSIX:

find . -depth -exec sh -c '
    for source; do
      case $source in ./*/*)
        target="$(printf %sz "${source#./}" | tr / -)";
        mv -i -- "$source" "${target%z}";;
      esac
    done
' _ {} +

Explicação: A instrução case omite o diretório atual e os subdiretórios de nível superior do diretório atual. target contém o nome do arquivo de origem ( $0 ) com o primeiro ./ removido e todas as barras substituídas por traços, além de um% finalz. O z final está lá, caso o nome do arquivo termine com uma nova linha: caso contrário, a substituição do comando o removerá.

Se o seu find não suporta -exec … + (OpenBSD, estou olhando para você):

find . -depth -exec sh -c '
    case $0 in ./*/*)
      target="$(printf %sz "${0#./}" | tr / -)";
      mv -i -- "$0" "${target%z}";;
    esac
' {} \;

Com bash (ou ksh93), você não precisa chamar um comando externo para substituir as barras por traços, você pode usar a expansão de parâmetro ksh93 com a construção de substituição de string ${VAR//STRING/REPLACEMENT} :

find . -depth -exec bash -c '
    for source; do
      case $source in ./*/*)
        source=${source#./}
        target="${source//\//-}";
        mv -i -- "$source" "$target";;
      esac
    done
' _ {} +
    
por 16.08.2012 / 20:24
3

Embora já haja boas respostas aqui, acho isso mais intuitivo, em bash :

find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;

Onde aaa é o seu diretório existente. Os arquivos que ele contém serão movidos para o diretório atual (deixando apenas diretórios vazios em aaa). As duas chamadas para tr manipulam diretórios e espaços (sem lógica para barras escapadas - um exercício para o leitor).

Eu considero isso mais intuitivo e relativamente fácil de ajustar. Ou seja Eu poderia alterar os parâmetros find se dissesse que quero encontrar apenas arquivos .png. Posso alterar as chamadas tr ou adicionar mais, conforme necessário. E eu posso adicionar echo antes de mv se eu quiser ver visualmente se ele fará a coisa certa (então repita sem o echo extra somente se as coisas parecerem corretas:

find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;

Note também que estou usando find aaa ... e não find . ... ou find aaa/ ... já que os dois últimos deixariam artefatos engraçados no nome final do arquivo.

    
por 24.05.2016 / 02:37
1
find . -mindepth 2 -type f -name '*' |
  perl -l000ne 'print $_;  s/\//-/g; s/^\.-/.\// and print' |
    xargs -0n2 mv 

Nota: isso não funcionará para o nome do arquivo que contém \n .
Isso, é claro, só move o tipo f files ...
Os únicos confrontos de nomes seriam de arquivos pré-existentes no pwd

Testado com este subconjunto básico

rm -fr junk
rm -f  junk*hello*

mkdir -p  junk/junkier/junkiest
touch    'hello    hello'
touch    'junk/hello    hello'
touch    'junk/junkier/hello    hello'
touch    'junk/junkier/junkiest/hello    hello'

Como resultado

./hello    hello
./junk-hello    hello
./junk-junkier-hello    hello
./junk-junkier-junkiest-hello    hello
    
por 16.08.2012 / 20:54
0

Aqui está uma versão ls . comentários bem-vindos. Ele funciona para nomes de arquivos malucos como este "j1ळ\nls1\n/j2/j3/j4/\nh  h\n" , mas estou realmente interessado em saber de quaisquer armadilhas. É mais um exercício de "pode o maligno %code% fazê-lo (robusto)?"

ls -AbQR1p * |                        # paths in "quoted" \escaped format
    sed -n '/":$/,${/.[^/]$/p}' |     # skip files in pwd, blank and dir/ lines
    { bwd="$PWD"; while read -n1;     # save base dir strip leading "quote
                        read -r p; do # read path
        printf -vs "${p%\"*}"         # strip trailing quote"(:)
        [[ $p == *: ]] && {           # this is a directory
            cd   "$bwd/$s"            # change into new directory
            rnp="${s//\//-}"-         # relative name prefix
        } || mv "$s" "$bwd/$rnp$s"
      done; }
    
por 17.08.2012 / 19:55
-1

Apenas insira o caminho filename + path para o comando tr . tr <replace> <with>

for FILE in 'find aaa -name "*.png"'
do
  NEWFILE='echo $FILE | tr '/' '-''
  cp -v $FILE $NEWFILE
done
    
por 16.08.2012 / 19:45
-2

Seguro para espaços em nomes de arquivos:

#!/bin/bash

/bin/find $PWD -type f | while read FILE
do
    _new="${FILE//\//-}"
    _new="${_new:1}"
    #echo $FILE
    #echo $_new

    /bin/mv "$FILE" "$_new"
done

Ran meu com cp em vez de mv por razões óbvias, mas deve produzir o mesmo.

    
por 16.08.2012 / 20:01

Tags