Ich möchte einen Dateibereich anhand eines Teils seines Namens auswählen, der zum Kopieren, Verschieben usw. verwendet werden soll.
Zufällige Beispielliste:
- alpha1a
- alpha2aa
- alpha3abc
- bravoA1
- bravoB2bb
- Charlie
- deltaA1fdfd
- deltaA2dafa
- deltaA2222
- deltaC1
- ... (eine Reihe anderer Dateien)
Ich möchte einen Befehl ausführen, der von alpha2* bis deltaA* geht und 2-9 in der Liste auswählt. Auf diese Weise kann ich einen Bereich basierend auf dem Namen auswählen, muss mir keine Gedanken darüber machen, wie viele Dateien es tatsächlich gibt, und bekomme keine zusätzlichen Dateien.
Antwort1
start=alpha2*
end=deltaA*
for file in *
do
if [[ $file > $start && ( $file < $end || $file == $end ) ]]; then
echo $file
fi
done
Anstelle von Echo können Sie die Dateien in einem Array speichern:
array+=($file)
Und verwenden Sie dann das Array zum Kopieren, Verschieben der Dateien … oder führen Sie den Befehl einfach innerhalb der For-Schleife aus.
Antwort2
Shell-Programmierung ist nicht sehr gut bei lexikographischen Vergleichen. In bash, ksh oder zsh können Sie die<
Bedingungsoperatorum zwei Zeichenfolgen zu vergleichen (POSIX sh verfügt nur über numerische Vergleiche).
run_in_range () {
start_at="$1"; end_before="$2"; command="$3"; shift 3
for item do
if [[ ! ($x < $start_at) && ($x < $end_before) ]]; then
"$command" "$item"
fi
done
}
run_in_range alpha2 deltaB some_command *
Beachten Sie, dass ich das erste ausgeschlossene Element als zweites Argument verwendet habe: deltaB
ist die kleinste Zeichenfolge, die nach allen Zeichenfolgen kommt, die mit beginnen deltaA
. Wenn Sie es als Argument übergeben möchten deltaA
, können Sie dies folgendermaßen tun.
run_in_inclusive_range () {
start_at="$1"; end_on_prefix="$2"; command="$3"; shift 3
for item do
if [[ ! ($x < $start_at) && ($x < $end_on_prefix || $x == "$end_on_prefix"*) ]]; then
"$command" "$item"
fi
done
}
run_in_inclusive_range alpha2 deltaA some_command *
Dieser Code ist nicht auf die Reihenfolge des Platzhalters angewiesen: Er funktioniert auch, wenn die Liste der Elemente nicht sortiert ist. Dies macht den Code robuster, aber etwas langsamer, da er die gesamte Liste durchgehen muss.
Hier ist ein alternativer Ansatz, der nur auf POSIX-Funktionen basiert. Er verwendet sort
zum Durchführen der Zeichenfolgenvergleiche. Dabei wird davon ausgegangen, dass in den Elementen keine Zeilenumbrüche vorhanden sind. Diese Funktion stellt die Standardeinstellung für IFS
und Globbing wieder her (es ist möglich, aber ärgerlich schwierig, die Umgebungseinstellung wiederherzustellen).
run_in_range () {
IFS='
'; set -f
start_at="$1"; end_before="$2"; command="$3"; shift 3
set $({
printf '%s1\n' "$@"
printf '%s0\n' "$start_at" "$end_before"
} | sort | sed '/0$/,/0$/ s/1$//p')
unset IFS; set +f
for item do
"$command" "$item"
done
}
Erläuterung:
- Erstellen Sie eine Liste, die die Elemente mit
1
angehängten Elementen und die Grenzen mit0
angehängten Elementen enthält. - Sortieren Sie die Liste.
- Drucken Sie die Zeilen zwischen den beiden Begrenzungslinien (die Zeilen, die mit enden
0
), aber nur die Zeilen, die mit enden1
(also die Elemente) und ohne das nachstehende1
.
Antwort3
Mir scheint, das Problem ist einfacher zu lösen, als es scheint:
Nehmen wir an, es gibt eine Möglichkeit, die Liste abzurufen, eine Datei pro Zeile.
Für das aktuelle Verzeichnis:
list="$(printf '%s\n' *)"
Hängen Sie einfach die Grenzwerte an die Liste an und sortieren Sie sie:
nl='
'
lim1="alpha2"
lim2="deltaA"
echo -- "$list""$nl""$lim1""$nl""$lim2" | sort
Beginnen Sie dann mit dem Drucken auf lim1 (achten Sie darauf, dass lim1 und lim2 nicht mit einem vorhandenen Dateinamen übereinstimmen) und stoppen Sie auf lim2.
Wählen Sie der Einfachheit halber außerdem die kürzeste Zeichenfolge aus, die direkt vor der zu druckenden Datei steht, und auch die kürzeste Zeichenfolge, die direkt nach der letzten auszuwählenden Datei sortiert wird.
Sinnvoll ist eine exklusive Liste (also eine ohne Grenzwerte),
es werden aber beide Lösungen angeboten.
Grundwerte:
list="$(printf '%s\n' *)"
lim1=alpha2
lim2=deltaA
Eine Lösung mit awk für eine inklusive Liste:
echo "awk----------------inclusive"
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
awk -v lim1="$lim1" -v lim2="$lim2" '$0==lim1,$0==lim2{print}'
Eine Lösung mit awk für eine exklusive Liste:
echo "awk----------------exclusive"
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
awk -v lim1="$lim1" -v lim2="$lim2" '$0==lim1{p=1;next};$0==lim2{p=0};p'
Eine Lösung mit der Shell für eine inklusive Liste:
echo "shell---------------inclusive"
show=0
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
while IFS="$nl" read -r line; do
[ "$line" = "$lim1" ] && show=1
[ "$show" = "1" ] && printf '%s\n' "$line"
[ "$line" = "$lim2" ] && show=0
done
Und eine exklusive Lösung für die Schale:
show=0
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
while read -r line; do
[ "$line" = "$lim2" ] && show=0
[ "$show" = "1" ] && printf '%s\n' "$line"
[ "$line" = "$lim1" ] && show=1
done