bash script - função loop

7

Eu consegui escrever o seguinte script:

#!/bin/bash

#files list
file1=/tmp/1wall_long.txt
file2=/tmp/1wall_test1.txt
file3=/tmp/1wall_test2.txt
file4=/tmp/1wall_test3.txt
file5=/tmp/3mt_long.txt
file6=/tmp/3mt_OpenSpace_test1.txt
file7=/tmp/3mt_OpenSpace_test2.txt
file8=/tmp/3mt_OpenSpace_test3.txt
file9=/tmp/3rooms_test1.txt
file10=/tmp/3rooms_test2.txt
file11=/tmp/3rooms_test3.txt
file12=/tmp/20mt_OpenSpace_test1.txt
file13=/tmp/20mt_OpenSpace_test2.txt
file14=/tmp/20mt_OpenSpace_test3.txt

#script for 1wall_long file
if [ ! -e "$file1" ]; then #check if the file exist
    echo "File 1wall_long.txt does not exist" #if not exist print echo output
else
    sed -i -e 's/- /-/g' $file1 #remove space on the first 10 values
    awk '{print $7}' $file1 > /tmp/1wall_long_S.txt #print the column number 7 and copy the output in a file
    rm $file1 #remove old file
fi

O script é repetido para todos os arquivos descritos na variável (basicamente eu tenho o mesmo script repetido 14 vezes com variáveis diferentes) Existe uma maneira melhor de fazer isso e qual é a melhor prática nessas situações?

    
por Federi 12.10.2015 / 12:13

3 respostas

6

Pessoalmente, eu evitaria codificar os nomes dos arquivos. Isso raramente é uma boa ideia e geralmente é melhor ter a opção de passar arquivos de destino como argumentos. Além disso, você está modificando o arquivo e excluindo o original. Isso não é eficiente, apenas modifique o arquivo na hora e imprima a sétima coluna sem precisar gravá-la no disco. Por exemplo:

#!/usr/bin/env bash

## Iterate over the file names given
for file in "$@"; do
    ## Get the output file's name. The ${file%.*} is
    ## the file's anme without its extension.
    outfile="${file%.*}"_S.txt
    ## If the file exists
    if [ -e "$file" ]; then
    ## remove the spaces and print the 7th column
    sed 's/- /-/g' "$file" | awk '{print $7}' > "$outfile" &&
        ## Delete the original but only if the step
        ## above was successful (that's what the && does)/
        rm "$file" 
    else
    ## If the file doesn't exist, print an error message
    echo "The file $file does not exist!"
    fi
done

Depois, você pode executar o script assim:

foo.sh /tmp/1wall_long.txt /tmp/1wall_test1.txt /tmp/1wall_test2.txt /tmp/1wall_test3.txt /tmp/20mt_OpenSpace_test1.txt /tmp/20mt_OpenSpace_test2.txt /tmp/20mt_OpenSpace_test3.txt /tmp/3mt_long.txt /tmp/3mt_OpenSpace_test1.txt /tmp/3mt_OpenSpace_test2.txt /tmp/3mt_OpenSpace_test3.txt /tmp/3rooms_test1.txt /tmp/3rooms_test2.txt /tmp/3rooms_test3.txt 

Se você quiser ter os nomes codificados, apenas use uma matriz como sugerido por @choroba:

#!/usr/bin/env bash

files=(/tmp/1wall_long.txt /tmp/1wall_test1.txt /tmp/1wall_test2.txt /tmp/1wall_test3.txt /tmp/20mt_OpenSpace_test1.txt /tmp/20mt_OpenSpace_test2.txt /tmp/20mt_OpenSpace_test3.txt /tmp/3mt_long.txt /tmp/3mt_OpenSpace_test1.txt /tmp/3mt_OpenSpace_test2.txt /tmp/3mt_OpenSpace_test3.txt /tmp/3rooms_test1.txt /tmp/3rooms_test2.txt /tmp/3rooms_test3.txt )


## Iterate over the file names given
for file in "${files[@]}"; do
    ## Get the output file's name. The ${file%.*} is
    ## the file's anme without its extension.
    outfile="${file%.*}"_S.txt
    ## If the file exists
    if [ -e "$file" ]; then
    ## remove the spaces and print the 7th column
    sed 's/- /-/g' "$file" | awk '{print $7}' > "$outfile" &&
        ## Delete the original but only if the step
        ## above was successful (that's what the && does)/
        rm "$file" 
    else
    ## If the file doesn't exist, print an error message
    echo "The file $file does not exist!"
    fi
done
    
por 12.10.2015 / 13:04
8

sem loop

primeiro use uma função

function sevenc
{


if [ ! -e "$1" ]; then #check if the file exist
    echo "File $1 does not exist" #if not exist print echo output
else
    sed -i -e 's/- /-/g' "$1" #remove space on the first 10 values
    awk '{print $7}' "$1" > /tmp/$(basename $1.txt)_S.txt #print the column number 7 and copy the output in a file
    rm "$1"  #remove old file
fi
}
  • quando o shell reconhece uma função, ele passará o argumento (se houver um para $ 1 $ 2 ... e assim por diante).
  • a propósito

's/- /-/g' "$1" #remove space on the first 10 values

NÃO, vire todo o espaço - para - na linha, esteja lá 1, 4, 10 ou 255.

então não há necessidade de mais var

sevenc /tmp/1wall_long.txt
sevenc /tmp/1wall_test1.txt
sevenc /tmp/1wall_test2.txt
sevenc /tmp/1wall_test3.txt
sevenc /tmp/3mt_long.txt
sevenc /tmp/3mt_OpenSpace_test1.txt
sevenc /tmp/3mt_OpenSpace_test2.txt
sevenc /tmp/3mt_OpenSpace_test3.txt
sevenc /tmp/3rooms_test1.txt
sevenc /tmp/3rooms_test2.txt
sevenc /tmp/3rooms_test3.txt
sevenc /tmp/20mt_OpenSpace_test1.txt
sevenc /tmp/20mt_OpenSpace_test2.txt
sevenc /tmp/20mt_OpenSpace_test3.txt

(desde que você não tenha mais uso de fileXX var).

sem loop (sol. 2)

Se você quiser passar mais argumentos e usar a otimização de Terdon, tente

function eight
{

file=$1
destdir=${2-/tmp} # use second arg if defined, else /tmp
exten=${3-S} 

if [ ! -e "$file" ]; then #check if the file exist
    echo "File $file does not exist" #if not exist print echo output
else
    sed  -e 's/- /-/g' "$file" \
    awk '{print $7}' "$1" > /"$destdir"/$(basename $1.txt)_"$exten".txt #print the column number 7 and copy the output in a file
    rm "$file"  #remove old file
fi
}

para ser chamado com

eight /tmp/1wall_test3.txt /my/projec/dir T ## will use /my/project/dir as dit, T as extension
eight /tmp/1wall_test1.txt /my/project ## will use /my/project as dir
eignt /tmp/1wall_test2.txt ## will use default value

essa função pode ser definida em .bashrc e ser usada interativamente.

com loop

while read f
do
if [ ! -e "$f" ]; then #check if the file exist
    echo "File $1 does not exist" #if not exist print echo output
else
    sed -i -e 's/- /-/g' "$f" #remove space on the first 10 values
    awk '{print $7}' "$f" > "/tmp/$(basename $f .txt)_S.txt" #print the column number 7 and copy the output in a file
    rm "$f"  #remove old file
fi
done <<EOF
/tmp/1wall_long.txt
/tmp/1wall_test1.txt
/tmp/1wall_test2.txt
/tmp/1wall_test3.txt
/tmp/3mt_long.txt
/tmp/3mt_OpenSpace_test1.txt
/tmp/3mt_OpenSpace_test2.txt
/tmp/3mt_OpenSpace_test3.txt
/tmp/3rooms_test1.txt
/tmp/3rooms_test2.txt
/tmp/3rooms_test3.txt
/tmp/20mt_OpenSpace_test1.txt
/tmp/20mt_OpenSpace_test2.txt
/tmp/20mt_OpenSpace_test3.txt
EOF
    
por 12.10.2015 / 12:29
6

Você pode usar uma matriz de arquivos e fazer um loop com for :

#!/bin/bash

files=(/tmp/1wall_long.txt
       /tmp/1wall_test1.txt
       /tmp/1wall_test2.txt
       /tmp/1wall_test3.txt
       /tmp/3mt_long.txt
       /tmp/3mt_OpenSpace_test1.txt
       /tmp/3mt_OpenSpace_test2.txt
       /tmp/3mt_OpenSpace_test3.txt
       /tmp/3rooms_test1.txt
       /tmp/3rooms_test2.txt
       /tmp/3rooms_test3.txt
       /tmp/20mt_OpenSpace_test1.txt
       /tmp/20mt_OpenSpace_test2.txt
       /tmp/20mt_OpenSpace_test3.txt )

for file in "${files[@]}" ; do
    if [ ! -e "$file" ]; then
        echo "File $file does not exist"
    else
        sed -i -e 's/- /-/g' "$file"
        # Use parameter expansion to create the new file name.
        newfile=${file%.txt}_S.txt
        awk '{print $7}' "$file" > "$newfile"
        rm "$file"
    fi
done
    
por 12.10.2015 / 12:37