So wählen Sie einen Dateibereich nach Teilnamen aus

So wählen Sie einen Dateibereich nach Teilnamen aus

Ich möchte einen Dateibereich anhand eines Teils seines Namens auswählen, der zum Kopieren, Verschieben usw. verwendet werden soll.

Zufällige Beispielliste:

  1. alpha1a
  2. alpha2aa
  3. alpha3abc
  4. bravoA1
  5. bravoB2bb
  6. Charlie
  7. deltaA1fdfd
  8. deltaA2dafa
  9. deltaA2222
  10. deltaC1
  11. ... (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: deltaBist 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 sortzum Durchführen der Zeichenfolgenvergleiche. Dabei wird davon ausgegangen, dass in den Elementen keine Zeilenumbrüche vorhanden sind. Diese Funktion stellt die Standardeinstellung für IFSund 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:

  1. Erstellen Sie eine Liste, die die Elemente mit 1angehängten Elementen und die Grenzen mit 0angehängten Elementen enthält.
  2. Sortieren Sie die Liste.
  3. Drucken Sie die Zeilen zwischen den beiden Begrenzungslinien (die Zeilen, die mit enden 0), aber nur die Zeilen, die mit enden 1(also die Elemente) und ohne das nachstehende 1.

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

verwandte Informationen