Copiando arquivos com um padrão específico

1

Estou tentando copiar arquivos de um local para outro e abaixo estão alguns exemplos:

aaa_bbb_ccc_ddd_cost_code_20140330.gz
aaa_bbb_ccc_ddd_revenue_zone_20140329.gz
aaa_bbb_ccc_ddd_benefit_extract_20140330.csv.gz
aaa_bbb_ccc_ddd_profit_zone_20150509.csv.gz
aaa_bbb_ccc_ddd_loss_zone_20140330.csv
aaa_bbb_ccc_ddd_username.csv.gz

da lista acima, os arquivos que devem ser copiados devem estar no seguinte formato:

aaa_bbb_ccc_ddd_cost[or]revenue[or]benefit[or]profit[or]loss_yyyymmdd.csv.gz

que significa que os arquivos

aaa_bbb_ccc_ddd_loss_zone_20140330.csv
aaa_bbb_ccc_ddd_username.csv.gz

não deve ser copiado.

Além disso, preciso atribuí-lo a uma variável e estou tentando algo assim, mas parece que não funciona:

FILENAME="egrep 'aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit)_code_[0-9]{8}.csv.gz'"

O motivo pelo qual estou tentando atribuir isso a uma variável é porque preciso usá-la posteriormente no código para algo assim:

SOURCE_DIR="/temp"
DESTN_DIR="/output"
FILENAME='egrep 'aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit)_code_[0-9]{8}.csv.gz''
echo "FILENAME is:" $FILENAME
for SAMPLE_FILE in $(ls "$SOURCE_DIR/$FILENAME")
do
cp $SAMPLE_FILE $DESTN_DIR
done

Existe uma maneira alternativa em que isso pode ser alcançado?

    
por user68112 21.05.2014 / 07:30

5 respostas

5

Use a opção find e sua -exec (aqui usando o GNU find para o predicado -regex ):

find . -regextype posix-egrep -regex '.*/aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit|loss)_[[:alpha:]]+_[0-9]+\.csv\.gz' -exec mv {} "$DESTN_DIR" \;

Nota:

  • find . diz a find para procurar por arquivos que estão começando no diretório atual.

  • Por padrão, o GNU find usa expressões regulares no estilo emacs. Eu prefiro -regextype posix-egrep mas você pode mudar para qualquer um dos estilos suportados com os quais você está familiarizado.

  • Uma expressão regular é usada para selecionar os arquivos: -regex '.*/aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit|loss)_[[:alpha:]]+_[0-9]+\.csv\.gz' . Isso permite que o prefixo padrão de aaa_bbb_ccc_ddd_ seguido por uma das palavras especificadas por (cost|revenue|benefit|profit|loss) , seguido por outra palavra não especificada, _[[:alpha:]]+ , seguido por uma data, _[0-9]+ , seguido pela extensão desejada de .csv.gz . Você pode querer ajustar isso.

  • Qualquer um desses arquivos encontrados será movido para o diretório de destino por meio de -exec mv {} "$DESTN_DIR" \; . Quando find localizar um arquivo correspondente, ele executará esse comando substituindo {} pelo nome do arquivo. Isso funcionará mesmo que os nomes dos arquivos tenham espaços, novas linhas ou outros caracteres difíceis.

Usando o estilo padrão (emacs) de expressão regular

O estilo padrão de regex para o GNU find requer algum escape dos operadores de agrupamento e alternação:

find . -regex '.*/aaa_bbb_ccc_ddd_\(cost\|revenue\|benefit\|profit\|loss\)_[[:alpha:]]+_[0-9]+\.csv\.gz' -exec echo mv {} targetdir \;

Mac OSX

A versão Mac OSX de find ( página man aqui ) suporta -regex mas não -regextype . Eu não ficaria surpreso, porém, se sua sintaxe regex exigisse algumas mudanças sutis.

IBM AIX 5

A página do manual para a versão do IBM AIX de find é aqui . Não suporta -regex .

    
por 21.05.2014 / 08:12
3

com zsh :

setopt extendedglob
source_dir="/temp"
destn_dir="/output"
pattern='aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit)_code_[0-9](#c8).csv.gz'
print -r "pattern is: $pattern"
cp -- $source_dir/$~pattern $destn_dir

padrões ksh93 podem expressar isso com

aaa_bbb_ccc_ddd_@(cost|revenue|benefit|profit)_code_{8}(\d).csv.gz

e ksh88 com:

aaa_bbb_ccc_ddd_@(cost|revenue|benefit|profit)_code_[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].csv.gz

No entanto, os operadores extended globbing não funcionam dentro de variáveis para evitar quebrar a compatibilidade POSIX:

echo @(a)

é uma sintaxe incorreta como por POSIX, então ksh pode torná-lo um novo operador glob. No entanto:

x='@(a)'
echo $x

é completamente especificado por POSIX e destina-se a produzir @(a) (com o valor padrão de IFS), não a se houver um arquivo chamado a no diretório atual.

Então, você precisaria recorrer ao uso de eval , o que pode ser difícil de acertar

pattern='aaa_bbb_ccc_ddd_@(cost|revenue|benefit|profit)_code_{8}([0-9]).csv.gz'
print -r "pattern is: $pattern"
eval 'cp -- "$source_dir"/'"$pattern"' "$destn_dir"'
    
por 21.05.2014 / 10:52
1

Isso pode ser feito em uma linha:

find /temp -maxdepth 1 -type f | \
 grep -P 'aaa_bbb_ccc_ddd_(cost|revenue|benefit|profit)_.*[0-9]{8}' | \
 xargs cp -t /output
  • find lista o conteúdo da pasta sem subpastas.
  • grep seus nomes de arquivos
  • e copie ( cp ) os mesmos do diretório de destino ( -t )

Você só precisa ajustar sua regex, porque na sua pergunta não foi 100% claro. Alguns arquivos têm .csv.gz , alguns .csv e alguns .gz .

    
por 21.05.2014 / 08:06
1
set -- ./aaa_bbb_ccc_ddd_[!ul]*
cp "$@" -t $location2
var=$*
    
por 21.05.2014 / 08:35
1

Qualquer shell moderno suporta diretamente o que você quer, não com a sintaxe básica do glob mas assim:

cp aaa_bbb_ccc_ddd_{cost,revenue,benefit,profit,loss}_[0-9]*.csv.gz destination_dir

Isso se expande em cinco argumentos, cada um dos quais é um glob da forma ..._keyword_<digits>...

Então, para responder sua segunda pergunta, veja como atribuir cada uma delas a uma variável:

for FNAME in aaa_bbb_ccc_ddd_{cost,revenue,benefit,profit,loss}_[0-9]*.csv.gz
do
    echo $FNAME
    if [ -e $FNAME ]
    then
        cp $FNAME <destination>
    fi
done

A verificação de existência ( if [ -e $FNAME ] ) é porque se qualquer um dos cinco globs não corresponder a nada, o glob será mantido como está e você receberá uma mensagem de erro.

    
por 21.05.2014 / 14:38