Bash-Skript - Schleifenfunktion

7

Ich habe folgendes Skript geschrieben:

#!/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

Das Skript wird für alle in der Variablen beschriebenen Dateien wiederholt (im Grunde habe ich dasselbe Skript 14 Mal mit verschiedenen Variablen wiederholt). Gibt es einen besseren Weg, dies zu tun, und was ist die beste Vorgehensweise in diesen Situationen?

Federi
quelle
1
Dies sollte wahrscheinlich auf codereview.stackexchange.com
Ethan Bierlein
1
@EthanBierlein nein, sollte es nicht. Wir migrieren niemals Themenfragen, es sei denn, das OP fordert dies an. Bash-Scripting ist hier sehr thematisch, daher kann diese Frage gerne bleiben.
Terdon

Antworten:

6

Persönlich würde ich es vermeiden, die Dateinamen fest zu codieren. Das ist selten eine gute Idee und es ist normalerweise besser, die Option zu haben, Zieldateien als Argumente zu übergeben. Außerdem ändern Sie die vorhandene Datei und löschen dann das Original. Das ist nicht effizient. Ändern Sie einfach die Datei im laufenden Betrieb und drucken Sie die 7. Spalte, ohne sie auf die Festplatte schreiben zu müssen. Zum Beispiel:

#!/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

Anschließend können Sie das Skript folgendermaßen ausführen:

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 

Wenn Sie möchten, dass die Namen fest codiert werden, verwenden Sie einfach ein Array, wie von @choroba vorgeschlagen:

#!/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
terdon
quelle
Hallo, danke für deine Antwort. Es klingt ähnlich wie die Antwort von Choroba. Ist es richtig?
Federi
Der zweite ist ja. Der Hauptunterschied besteht darin, dass ich es vermeide, eine Datei zu erstellen, um sie später zu löschen. Sie sed -ifügen nur zusätzliche Arbeit hinzu. Da Sie die Datei trotzdem löschen, gibt es keinen Grund für die Verwendung, -iwodurch nur ein unnötiger Schreibvorgang auf die Festplatte hinzugefügt wird. Mein erster Vorschlag ist anders: Anstatt die Dateinamen in das Skript zu schreiben, übergeben Sie sie als Argumente. Auf diese Weise können Sie die Dateien, auf denen es ausgeführt wird, einfach ändern.
Terdon
Entschuldigung, letzter. Was bedeutet% in "$ {file%. *}"
Federi
1
@Federi, ${var%pattern}gibt den Wert von $varmit patternvom Ende entfernt zurück. Also ${file%.*}wird der letzte Punkt und alles danach entfernt. (Hinweis: foo.bar.bazwird foo.bar. Wenn Sie verwenden %%, wird es foo.)
nyuszika7h
8

schleifenlos

Verwenden Sie zuerst eine Funktion

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
}
  • Wenn die Shell eine Funktion erkennt, übergibt sie ein Argument (falls vorhanden an $ 1 $ 2 ... und so weiter).
  • Apropos

's / - / - / g' "$ 1" #Entfernen Sie das Leerzeichen auf den ersten 10 Werten

NEIN, es dreht sich alles space-um -auf der Linie, sei da 1, 4, 10 oder 255.

dann keine Notwendigkeit mehr 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

(vorausgesetzt, Sie verwenden fileXX var nicht mehr).

schleifenlos (Sol. 2)

Wenn Sie mehr Argumente übergeben möchten und Terdons Optimierungsversuch verwenden möchten

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
}

angerufen werden mit

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

Diese Funktionen können in .bashrc definiert und interaktiv verwendet werden.

mit Schleife

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
Archemar
quelle
Danke für deine Antwort. Ich habe eine Frage "Schleifen" Option: 1. Muss ich die Variable $ 1 definieren? 2.Der Name Sevenc ist wohl ein Beispiel und ich kann den von mir bevorzugten Namen verwenden. 3.Wie kann ich den Basisnamen für jede Ausgabe ändern?
Federi
siehe bearbeiten für Sol 2.
Archemar
6

Sie können ein Array von Dateien verwenden und eine Schleife darüber erstellen mit 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
Choroba
quelle
1
Das kommt mir aufgrund meines geringen Wissens besser bekannt :) Danke Nur eine kurze Frage, was @ in $ {files [@]} bedeutet.
Federi
2
$ {files [@]} gibt Ihnen das gesamte Array, da $ {files} nur das erste Element enthält.
Rexkogitans