Come estrarre i primi due caratteri di una stringa nello script di shell?


123

Ad esempio, dato:

USCAGoleta9311734.5021-120.1287855805

Voglio estrarre solo:

US

6
Grazie a tutti. Ho finito per usare "cut -c1-2", onestamente non sapevo nemmeno che "cut" fosse lì. Vorrei dire che sono abbastanza esperto a riga di comando, ma a quanto pare ho molto da imparare.
Greg

1
@ Greg, tieni presente che il taglio viene eseguito come un processo separato: sarà più lento della soluzione di bash interna che ho pubblicato insieme ad essa nella mia risposta. Ciò non farà alcuna differenza a meno che non si elaborino enormi set di dati, ma è necessario tenerlo a mente.
paxdiablo

Modifica In realtà, penso che questa riga di codice verrà probabilmente eseguita circa 50.000 volte per rapporto. Quindi potrei semplicemente utilizzare il metodo Bash interno, che come hai detto salverà alcune risorse molto necessarie.
Greg

Risposte:


180

Probabilmente il metodo più efficiente, se stai usando la bashshell (e sembra che tu lo sia, in base ai tuoi commenti), è usare la variante della sottostringa dell'espansione dei parametri:

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

Saranno shorti primi due caratteri di long. Se longè più corto di due caratteri, shortsarà identico.

Questo metodo in-shell di solito è migliore se lo farai molto (come 50.000 volte per rapporto come hai menzionato) poiché non c'è alcun sovraccarico di creazione del processo. Tutte le soluzioni che utilizzano programmi esterni soffriranno di tale sovraccarico.

Se vuoi anche assicurarti una lunghezza minima , potresti riempirla prima con qualcosa del tipo:

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

Ciò garantirebbe che qualsiasi cosa di lunghezza inferiore a due caratteri fosse riempita sulla destra con punti (o qualcos'altro, semplicemente cambiando il carattere usato durante la creazione tmpstr). Non è chiaro se ne hai bisogno, ma ho pensato di inserirlo per completezza.


Detto questo, ci sono molti modi per farlo con programmi esterni (ad esempio se non hai a bashdisposizione), alcuni dei quali sono:

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/')

I primi due ( cute head) sono identici per una stringa a riga singola: fondamentalmente entrambi restituiscono solo i primi due caratteri. Si differenziano per il fatto che cutti daranno i primi due caratteri di ogni riga e headti daranno i primi due caratteri dell'intero input

Il terzo utilizza la funzione di awksottostringa per estrarre i primi due caratteri e il quarto utilizza i sedgruppi di acquisizione (utilizzando ()e \1) per acquisire i primi due caratteri e sostituire l'intera riga con essi. Sono entrambi simili a cut: forniscono i primi due caratteri di ogni riga nell'input.

Niente di tutto ciò ha importanza se sei sicuro che il tuo input sia una singola riga, hanno tutti lo stesso effetto.


Preferirei usare printf '%s'invece di echonel caso in cui ci siano caratteri strani nella stringa: stackoverflow.com/a/40423558/895245 Per l'ossessione POSIX: head -cnon è POSIX cut -ce non lo awk substrsono, sed \1non sono sicuro.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli 新疆 改造 中心 996ICU 六四 事件 usando printf, non hai nemmeno bisogno di un programma aggiuntivo. Vedi la mia risposta .
bschlueter

60

il modo più semplice è

${string:position:length}

Dove questo estrae la $lengthsottostringa da $stringa $position.

Questo è un builtin bash quindi awk o sed non è richiesto.


Questo è il modo più breve, dolce e semplice per ottenere la sottostringa.
ani627

34

Hai ottenuto più risposte buone e mi piacerebbe andare con la Bash incorporato me stesso, ma dal momento che hai chiesto sede awke ( quasi ) nessun altro offerto soluzioni basate su di essi, vi questi offrono:

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

e

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

La awksi dovrebbe essere abbastanza ovvio, ma qui è una spiegazione del seduno:

  • sostituire "s /"
  • il gruppo "()" di due caratteri qualsiasi ".." che inizia all'inizio della riga "^" e seguito da qualsiasi carattere "." ripetuto zero o più volte "*" (i backslash sono necessari per sfuggire ad alcuni caratteri speciali)
  • da "/" il contenuto del primo (e unico, in questo caso) gruppo (qui il backslash è un escape speciale che si riferisce a una sottoespressione corrispondente)
  • fatto "/"

1
In awk le stringhe iniziano con l'indice 1, quindi dovresti usare substr($0,1,2).
Isaac

8

Se sei dentro bash, puoi dire:

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

Questo potrebbe essere proprio quello di cui hai bisogno ...


questa è la risposta più facile e più semplice! ha funzionato a meraviglia
aloha

7

Solo grep:

echo 'abcdef' | grep -Po "^.."        # ab

Si adatta alle mie esigenze. Puoi rimuovere l' -Popzione per renderlo più breve. Tutte le espressioni regolari capiranno quel modello.
datashaman

6

Puoi usare printf:

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

5

colrm - rimuove le colonne da un file

Per lasciare i primi due caratteri, è sufficiente rimuovere le colonne a partire da 3

cat file | colrm 3

4

Abbastanza tardi in effetti, ma eccolo qui

sed 's/.//3g'

O

awk NF=1 FPAT=..

O

perl -pe '$_=unpack a2'

2

Se vuoi usare lo scripting della shell e non fare affidamento su estensioni non posix (come i cosiddetti bashismi), puoi usare tecniche che non richiedono strumenti esterni di fork come grep, sed, cut, awk, ecc., Che poi rendere il tuo script meno efficiente. Forse l'efficienza e la portabilità posix non sono importanti nel tuo caso d'uso. Ma nel caso lo sia (o semplicemente come una buona abitudine), puoi usare il seguente metodo di opzione di espansione dei parametri per estrarre i primi due caratteri di una variabile di shell:

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

Utilizza l' espansione del parametro "prefisso più piccolo" per rimuovere i primi due caratteri (questa è la ${var#??}parte), quindi l' espansione del parametro "suffisso più piccolo" (la ${var%parte) per rimuovere la stringa di tutti i caratteri tranne i primi due dall'originale valore.

Questo metodo è stato precedentemente descritto in questa risposta alla domanda "Shell = Controlla se la variabile inizia con #". La risposta descrive anche un paio di metodi di espansione dei parametri simili che possono essere utilizzati in un contesto leggermente diverso da quello che si applica alla domanda originale qui.


La migliore risposta, dovrebbe essere in cima. niente forchette, niente bashismi. funziona anche con piccoli gusci come il trattino.
exore

1

Se il tuo sistema utilizza una shell diversa (non bash), ma il tuo sistema lo ha bash, puoi comunque utilizzare la manipolazione intrinseca delle stringhe bashinvocando bashcon una variabile:

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;"

Utilizza lo stesso metodo della risposta principale , invocando solo bashse non lo stai già utilizzando.
palswim

Sfortunatamente, questo viene fornito con tutto il sovraccarico di invocare un altro processo, ma a volte questo sovraccarico non ha importanza quanto la semplicità e la familiarità.
palswim

1

Solo per divertimento, ne aggiungerò alcune che, sebbene siano troppo complicate e inutili, non sono state menzionate:

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]'


0

se mystring = USCAGoleta9311734.5021-120.1287855805

print substr(mystring,0,2)

ci stamperebbe

dove 0 è la posizione iniziale e 2 è il modo in cui leggere i caratteri


Dimmi ... non è GW-BASIC? Oh, aspetta, ecco awk. Scusa, all'inizio non potevo dirlo.
In pausa fino a nuovo avviso.

0

È questo quello che cerchi?

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

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

rif: substr


1
dato che è probabile che lo chiami dalla shell, una forma migliore sarebbeperl -e 'print substr $ARGV[0], 0, 2' 'USCAGoleta9311734.5021-120.1287855805'
Chas. Owens
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.