
Ich versuche eine Lösung fürDasFrage. Meine bisherige Herangehensweise an dieses Problem ist wie folgt.
- Hängen Sie alle Zeichen aneinander, um eine lange Zeichenfolge zu erstellen.
- Entfernen Sie nach dem obigen Schritt alle Leerzeichen oder Tabulatoren, sodass wir nur eine große Zeichenfolge haben.
Ich konnte die oben genannten Schritte mit dem folgenden Befehl durchführen.
column -s '\t' inputfile | tr -d '[:space:]'
Für eine Eingabedatei wie diese,
1 0 0 0 0 0
0 1 1 1 0 0
Nach dem Anwenden des obigen Befehls habe ich die Werte als:
100000011100
Jetzt versuche ich, in diesem großen String den folgenden Ansatz anzuwenden.
Extrahieren Sie jedes 6. Zeichen (wie es der ursprüngliche OP möchte) und hängen Sie es bis zum Ende der Zeichenfolge an ein Array-Element an.
Im Grunde versuche ich mit dem obigen Schritt, die Array-Elemente wie folgt zu erstellen:
10
(1. und 7. Zeichen ), 01
(2. und 8. Zeichen ), 01
(3. und 9. Zeichen ), 01
(4. und 10. Zeichen ), 00
(5. und 11. Zeichen ), 00
(6. und 12. Zeichen ).
Meine Frage ist also: Wie kann ich jedes n- te Zeichen extrahieren, damit ich es einem Array hinzufügen und weitermachen kann? (in diesem Fall n=6).
Antwort1
Zwei Linien
Hier ist eine reine bash
Lösung, die ein Array erzeugt bash
:
s="100000011100"
array=($(
for ((i=0; i<${#s}-6; i++))
do
echo "${s:$i:1}${s:$((i+6)):1}"
done
))
echo "${array[@]}"
Dies führt zu derselben Ausgabe wie in der Frage:
10 01 01 01 00 00
Das Schlüsselelement hierbei ist die Verwendung von BashsTeilstringerweiterung. Bash ermöglicht die Extraktion von Teilzeichenfolgen aus einer Variablen, beispielsweise parameter
über ${parameter:offset:length}
. In unserem Fall wird der Offset durch die Schleifenvariable bestimmt i
und die Länge beträgt immer 1
.
Allgemeine Lösung für eine beliebige Anzahl von Zeilen
Nehmen wir beispielsweise an, dass unsere ursprüngliche Zeichenfolge 18 Zeichen hat und wir das i-te, das i+6-te und das i+12-te Zeichen für i von 0 bis 5 extrahieren möchten. Dann gilt:
s="100000011100234567"
array=($(
for ((i=0; i<6; i++))
do
new=${s:$i:1}
for ((j=i+6; j<${#s}; j=j+6))
do
new="$new${s:$j:1}"
done
echo "$new"
done
))
echo "${array[@]}"
Das Ergebnis ist:
102 013 014 015 006 007
Derselbe Code kann auf eine beliebige Anzahl von 6-stelligen Zeilen erweitert werden. Er besteht beispielsweise aus s
drei Zeilen (18 Zeichen):
s="100000011100234567abcdef"
Die Ausgabe lautet dann:
102a 013b 014c 015d 006e 007f
Antwort2
Verwendung von perl
:
$ echo 100000011100 | perl -nle '
for ($i = 0; $i < length()/2; $i++) {
print substr($_,$i,1), substr($_,$i+6,1);
}
'
10
01
01
01
00
00
Es funktioniert für zwei Zeilen. Wenn Sie mit einer beliebigen Anzahl von Zeilen arbeiten möchten, sollten Sie die Zeilen direkt verarbeiten, anstatt große Zeichenfolgen zu erstellen. Mit dieser Eingabe:
1 0 0 0 0 0
0 1 1 1 0 0
0 0 0 0 0 0
Versuchen:
$ perl -anle '
for ($i = 0; $i <= $#F; $i++) {
push @{$h{$i}}, $F[$i];
}
END {
print @{$h{$_}} for keys %h;
}
' file
000
010
000
100
010
010
Antwort3
Als Shell-Lösung getopts
ist es wahrscheinlich am einfachsten. Die Sache getopts
ist, dass es POSIX-spezifiziert ist, genau das zu tun, was Sie verlangen – einen Byte-Stream in einer Shell-Schleife zu verarbeiten. Ich weiß, das klingt komisch, denn wenn Sie wie ich sind, bevor ich das gelernt habe, denken Sie wahrscheinlich:also, ich dachte, es sollte Befehlszeilenschalter verarbeiten.Das stimmt, aber das Erste trifft auch zu. Bedenken Sie:
-thisisonelongstringconsistingofseparatecommandlineswitches
Ja, getopts
das muss man handhaben. Es muss das in einer Schleife Zeichen für Zeichen aufteilen und jedes Zeichen entweder in der Shell-Variable $OPTARG
oder in einer anderen, die Sie namentlich angeben, an Sie zurückgeben, je nachdem, wie spezifisch Sie es beim Aufrufen machen. Darüber hinaus muss es Fehler in Shell-Variablen zurückgeben undseinen Fortschritt speichernwenn dies in der Shell-Variable der Fall ist, $OPTIND
kann esgenau dort fortfahren, wo es aufgehört hatwenn Sie es irgendwie ansprechen können. Und es muss die gesamte Aufgabe erledigen, ohne eine einzige Subshell aufzurufen.
Nehmen wir also an, wir haben:
arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done
Hmmm... ich frage mich, ob es funktioniert hat?
echo "$((${#arg}/6))" "$#"
482 482
Das ist schön...
eval '
printf %.1s\\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4
Wie Sie also sehen, getopts
legt der Befehl das Array für jedes sechste Byte in der Zeichenfolge vollständig fest. Und es müssen nicht unbedingt Zahlen wie diese sein – noch müssen es Shell-sichere Zeichen sein – und Sie müssen nicht einmal die Zielzeichen angeben, wie ich es oben bei 01234565789
beiden getan habe. Ich habe dies wiederholt in vielen Shells getestet und sie funktionieren alle einfach. Es gibt einige Eigenheiten – es bash
wirft das erste Zeichen weg, wenn es ein Leerzeichen ist – dash
akzeptiert den :
Doppelpunkt als angegebenen Parameter, obwohl dies so ziemlich das einzige ist, was POSIX ausdrücklich verbietet. Aber nichts davon ist wichtig, da getopts
der Wert des aktuellen opt-Zeichens trotzdem hinterlegt wird, $OPTARG
selbst wenn es Ihnen einen Fehler zurückgibt(dargestellt durch ein ?, das Ihrer angegebenen opt-Variable zugewiesen ist)und ansonsten explizit aufgehoben, $OPTARG
es sei denn, Sie haben deklariert, dass eine Option ein Argument haben sollte. Und die Sache mit den Leerzeichen ist eigentlich eine gute Sache - es verwirft nurführendPlatz, was hervorragend ist, denn wenn Sie mit unbekannten Werten arbeiten, können Sie Folgendes tun:
getopts : o -" $unknown_value"
... um die Schleife zu starten, ohne dass die Gefahr besteht, dass das erste Zeichen tatsächlich in Ihrer akzeptierten Argumentzeichenfolge enthalten ist – was dazu führen würde, dass getopts
das Ganze auf $OPTARG
einmal als Argument eingefügt wird.
Hier ist ein weiteres Beispiel:
OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"
do printf '\\%04o' "'$OPTARG"; done
\0040\0150\0071\0365\0320\0070\0161\0064\0274\0115\0012\0215\0222\0271\0146\0057\0166
Ich habe es $OPTIND=1
in der ersten Zeile eingestellt, weil ich es gerade verwendet habe getopts
und bis Sie es zurücksetzen, erwartet es, dass sein nächster Aufruf dort fortgesetzt wird, wo es aufgehört hat – es will es "${arg2}"
mit anderen Worten. Aber ich habe keine Lust, nachzugeben, und mache jetzt etwas anderes, also lasse ich es durch Zurücksetzen wissen, $OPTIND
an welchem Punkt es bereit ist, weiterzumachen.
In diesem Beispiel habe ich zsh
- was kein Problem mit einem führenden Leerzeichen ist - verwendet, sodass das erste Zeichen Oktal 40 ist - das Leerzeichen. Normalerweise verwende ich es getopts
jedoch nicht auf diese Weise - ich verwende es normalerweise, umvermeidenmache write()
für jedes Byte ein und weise stattdessen seine Ausgabe – die in einer Variable kommt – einer anderen Shell-Variable zu – wie ich es oben mit „ set
after a fashion“ gemacht habe. Wenn ich dann fertig bin, kann ich die ganze Zeichenfolge nehmen und wenn ich das tue, entferne ich normalerweise das erste Byte.
Antwort4
sed
ist das Erste, was mir in den Sinn kommt.
$ echo 1234567890abcdefghijklmnopqrstuvwxyz | sed 's/.\{5\}\(.\)/\1/g'
6bhntz
Bringen Sie 5 Charaktere in Einklang, erfassen Sie den 6. und ersetzen Sie sie alle durch den erfassten Charakter.
Dies führt jedoch zu einem Problem, wenn die Länge der Zeichenfolge kein genaues Vielfaches von 6 ist:
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{5\}\(.\)/\1/g'
6bhntuvwxy
Dies können wir jedoch beheben, indem wir Folgendes sed
ein wenig ändern:
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{1,5\}\(.\{0,1\}\)/\1/g'
6bhnt
Aufgrund der gierigen Natur regulärer Ausdrücke werden bei den Übereinstimmungen mit variabler Länge so viele Übereinstimmungen wie möglich gefunden. Wenn nichts zum Erfassen übrig bleibt, wird es nicht erfasst und die Zeichen werden einfach gelöscht.