Wie extrahiere ich die ersten beiden Zeichen einer Zeichenfolge in Shell-Skripten?

122

Zum Beispiel gegeben:

USCAGoleta9311734.5021-120.1287855805

Ich möchte nur extrahieren:

US
Greg
quelle
6
Vielen Dank an alle. Am Ende habe ich 'cut -c1-2' verwendet, ehrlich gesagt wusste ich nicht einmal, dass 'cut' da ist. Ich möchte sagen, dass ich ziemlich erfahren in der Kommandozeile bin - aber anscheinend muss ich noch viel lernen.
Greg
1
@ Greg, sei dir nur bewusst, dass das Schneiden als separater Prozess ausgeführt wird - es ist langsamer als die interne Bash-Lösung, die ich in meiner Antwort daneben gepostet habe. Das macht keinen Unterschied, es sei denn, Sie verarbeiten große Datenmengen, aber Sie müssen dies berücksichtigen.
Paxdiablo
Bearbeiten Eigentlich denke ich, dass diese Codezeile wahrscheinlich ungefähr 50.000 Mal pro Bericht ausgeführt wird. Ich könnte mich also für die interne Bash-Methode entscheiden, die, wie Sie sagten, einige dringend benötigte Ressourcen spart.
Greg
Verwandte: unix.stackexchange.com/questions/3454/…
Ciro Santilli 7 冠状 病 六四 事件 7

Antworten:

179

Wenn Sie die bashShell verwenden (und dies scheint, basierend auf Ihren Kommentaren), ist es wahrscheinlich die effizienteste Methode, die Sub-String-Variante der Parametererweiterung zu verwenden:

pax> long="USCAGol.blah.blah.blah"
pax> short="${long:0:2}" ; echo "${short}"
US

Dies shortwerden die ersten beiden Zeichen von sein long. Wenn longes kürzer als zwei Zeichen ist, shortist es identisch.

Diese In-Shell-Methode ist normalerweise besser, wenn Sie viel tun (wie Sie bereits erwähnt 50.000 Mal pro Bericht), da kein Aufwand für die Prozesserstellung anfällt. Alle Lösungen, die externe Programme verwenden, leiden unter diesem Aufwand.

Wenn Sie auch eine Mindestlänge sicherstellen möchten , können Sie diese vorab mit etwas ausfüllen:

pax> long="A"
pax> tmpstr="${long}.."
pax> short="${tmpstr:0:2}" ; echo "${short}"
A.

Dies würde sicherstellen, dass rechts weniger als zwei Zeichen mit Punkten (oder etwas anderem, nur durch Ändern des beim Erstellen verwendeten Zeichens tmpstr) aufgefüllt werden . Es ist nicht klar, dass Sie das brauchen, aber ich dachte, ich würde es der Vollständigkeit halber einfügen.


Es gibt jedoch eine Reihe von Möglichkeiten, dies mit externen Programmen zu tun (z. B. wenn Ihnen diese nicht zur bashVerfügung stehen). Einige davon sind:

short=$(echo "${long}" | cut -c1-2)
short=$(echo "${long}" | head -c2)
short=$(echo "${long}" | awk '{print substr ($0, 0, 2)}'
short=$(echo "${long}" | sed 's/^\(..\).*/\1/')

Die ersten beiden ( cutund head) sind für eine einzeilige Zeichenfolge identisch - beide geben Ihnen im Grunde nur die ersten beiden Zeichen zurück. Sie unterscheiden sich darin, cutdass Sie die ersten beiden Zeichen jeder Zeile und headdie ersten beiden Zeichen der gesamten Eingabe erhalten

Der dritte verwendet die awkSub-String-Funktion, um die ersten beiden Zeichen zu extrahieren, und der vierte verwendet sedErfassungsgruppen (mit ()und \1), um die ersten beiden Zeichen zu erfassen und die gesamte Zeile durch diese zu ersetzen. Sie sind beide ähnlich cut- sie liefern die ersten beiden Zeichen jeder Zeile in der Eingabe.

Nichts davon ist von Bedeutung, wenn Sie sicher sind, dass Ihre Eingabe eine einzelne Zeile ist. Alle haben den gleichen Effekt.

paxdiablo
quelle
Ich würde lieber verwenden, als printf '%s'für den echoFall, dass es seltsame Zeichen in der Zeichenfolge gibt: stackoverflow.com/a/40423558/895245 Für die POSIX besessen: head -cist nicht POSIX cut -cund awk substrsind sed \1nicht sicher.
Ciro Santilli 法轮功 冠状 病 六四 事件 7
1
@CiroSantilli print 改造 6 996ICU 六四 事件 Mit printf benötigen Sie nicht einmal ein zusätzliches Programm. Siehe meine Antwort .
bschlueter
60

Der einfachste Weg ist

${string:position:length}

Wo dies $lengthTeilzeichenfolge aus $stringat extrahiert $position.

Dies ist eine eingebaute Bash, so dass kein Awk oder Sed erforderlich ist.

ennuikiller
quelle
Dies ist der kurze, süße und einfachste Weg, um den Teilstring zu bekommen.
Ani627
34

Sie haben mehrere gute Antworten bekommen und ich würde mit dem Bash builtin mich gehen, aber da Sie gefragt , sedund awkund ( fast ) niemand sonst Lösungen auf ihnen angeboten basiert, biete ich Ihnen diese:

echo "USCAGoleta9311734.5021-120.1287855805" | awk '{print substr($0,0,2)}'

und

echo "USCAGoleta9311734.5021-120.1287855805" | sed 's/\(^..\).*/\1/'

Der awkeine sollte ziemlich offensichtlich sein, aber hier ist eine Erklärung des sedeinen:

  • Ersatz "s /"
  • die Gruppe "()" von zwei beliebigen Zeichen ".." beginnend am Anfang der Zeile "^" und gefolgt von einem beliebigen Zeichen "." null oder mehrmals "*" wiederholt (die Backslashes werden benötigt, um einige der Sonderzeichen zu umgehen)
  • durch "/" den Inhalt der ersten (und in diesem Fall einzigen) Gruppe (hier ist der Backslash ein spezielles Escape, das sich auf einen übereinstimmenden Unterausdruck bezieht)
  • getan "/"
Bis auf weiteres angehalten.
quelle
1
In awk beginnen Zeichenfolgen bei Index 1, daher sollten Sie verwenden substr($0,1,2).
Isaac
8

Wenn Sie dabei sind bash, können Sie sagen:

bash-3.2$ var=abcd
bash-3.2$ echo ${var:0:2}
ab

Dies kann genau das sein, was Sie brauchen ...

Dominic Mitchell
quelle
Diese einfachste und einfachste Antwort! arbeitete wie ein Zauber
Aloha
7

Nur grep:

echo 'abcdef' | grep -Po "^.."        # ab
Amir Mehler
quelle
Passt zu meinen Bedürfnissen. Sie können die -POption entfernen , um sie zu verkürzen. Alle regulären Ausdrücke werden dieses Muster verstehen.
Datashaman
6

Sie können verwenden printf:

$ original='USCAGoleta9311734.5021-120.1287855805'
$ printf '%-.2s' "$original"
US
bschlueter
quelle
5

colrm - Spalten aus einer Datei entfernen

Um die ersten beiden Zeichen zu belassen, entfernen Sie einfach die Spalten ab 3

cat file | colrm 3
Ian Yang
quelle
4

Ziemlich spät, aber hier ist es

sed 's/.//3g'

Oder

awk NF=1 FPAT=..

Oder

perl -pe '$_=unpack a2'
Steven Penny
quelle
2

Wenn Sie Shell-Scripting verwenden möchten und sich nicht auf Nicht-Posix-Erweiterungen (wie sogenannte Bashisms) verlassen möchten, können Sie Techniken verwenden, für die keine externen Tools wie grep, sed, cut, awk usw. erforderlich sind Machen Sie Ihr Skript weniger effizient. Möglicherweise sind Effizienz und Posix-Portabilität in Ihrem Anwendungsfall nicht wichtig. Falls dies jedoch der Fall ist (oder nur eine gute Angewohnheit ist), können Sie die folgenden beiden Zeichen einer Shell-Variablen mit der folgenden Methode zur Parametererweiterungsoption extrahieren:

$ sh -c 'var=abcde; echo "${var%${var#??}}"'
ab

Hierbei wird die Parametererweiterung "kleinstes Präfix" verwendet , um die ersten beiden Zeichen (dies ist der ${var#??}Teil) zu entfernen , und dann die Parametererweiterung "kleinstes Suffix" (der ${var%Teil), um die Zeichenfolge mit Ausnahme der ersten zwei Zeichen aus dem Original zu entfernen Wert.

Diese Methode wurde zuvor in dieser Antwort auf die Frage "Shell = Überprüfen, ob die Variable mit # beginnt" beschrieben. Diese Antwort beschreibt auch einige ähnliche Parametererweiterungsmethoden, die in einem etwas anderen Kontext verwendet werden können als der, der hier für die ursprüngliche Frage gilt.

Juan
quelle
Beste Antwort, sollte oben sein. Keine Gabeln, keine Bashismen. funktioniert auch mit kleinen Muscheln wie Dash.
Exore
1

Wenn Ihr System eine andere Shell verwendet (nicht bash), Ihr System jedoch eine andere bash, können Sie die inhärente Zeichenfolgenmanipulation von weiterhin verwenden, bashindem Sie basheine Variable aufrufen :

strEcho='echo ${str:0:2}' # '${str:2}' if you want to skip the first two characters and keep the rest
bash -c "str=\"$strFull\";$strEcho;"
palswim
quelle
Dies verwendet dieselbe Methode wie die Hauptantwort und wird nur aufgerufen, bashwenn Sie sie noch nicht verwenden.
Palswim
Leider ist dies mit dem gesamten Aufwand für das Aufrufen eines anderen Prozesses verbunden, aber manchmal ist dieser Aufwand weniger wichtig als Einfachheit und Vertrautheit.
Palswim
1

Nur um Spaß zu haben, füge ich einige hinzu, die zwar überkompliziert und nutzlos sind, aber nicht erwähnt wurden:

head -c 2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

echo 'USCAGoleta9311734.5021-120.1287855805' | dd bs=2 count=1 status=none

sed -e 's/^\(.\{2\}\).*/\1/;' <( echo 'USCAGoleta9311734.5021-120.1287855805')

cut -c 1-2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

python -c "print(r'USCAGoleta9311734.5021-120.1287855805'[0:2])"

ruby -e 'puts "USCAGoleta9311734.5021-120.1287855805"[0..1]'
Matias Barrios
quelle
0
perl -ple 's/^(..).*/$1/'
dsm
quelle
Sie haben vergessen, die Zeichenfolge darin wiederzugeben.
Chas. Owens
0

if mystring = USCAGoleta9311734.5021-120.1287855805

print substr(mystring,0,2)

würde US drucken

Dabei ist 0 die Startposition und 2 die Anzahl der zu lesenden Zeichen

Jambobond
quelle
Sag ... ist das nicht GW-BASIC? Oh, warte, das ist awk. Entschuldigung, ich konnte es zuerst nicht sagen.
Bis auf weiteres angehalten.
0

Ist es das, wonach du suchst?

my $string = 'USCAGoleta9311734.5021-120.1287855805';

my $first_two_chars = substr $string, 0, 2;

ref: substr

draegtun
quelle
1
Angesichts der Tatsache, dass er / sie dies wahrscheinlich von der Shell aus aufruft, wäre eine bessere Formperl -e 'print substr $ARGV[0], 0, 2' 'USCAGoleta9311734.5021-120.1287855805'
Chas. Owens