Manipolazione delle stringhe bash


9

Ho letto alcune altre domande sulla manipolazione delle stringhe bash del piping ma sembrano essere applicazioni specializzate.

In sostanza, c'è un modo per rendere il seguito più semplice?

invece di

$ string='hello world'; string2="${string// /_}"; echo "${string2^^}"
HELLO_WORLD

qualcosa di simile a

$ echo 'hello world' | $"{-// /_}" | "${ -^^}"
HELLO_WORLD

Modifica Sono interessato a rimanere nelle manipolazioni di bash, se possibile, per mantenere la velocità (al contrario di sed / awk che hanno la tendenza a rallentare notevolmente i miei script)

Edit2: @jimmij

Mi piace il secondo esempio e mi ha portato a fare una funzione.

bash_m() { { read x; echo "${x// /_}"; } | { read x; echo "${x^^}"; }; }
echo hello world | bash_m
HELLO_WORLD

1
perché pensi che sed / awk sarebbe lento per questo scopo? Sono veloci come arrivano.
mkc,

1
@Ketan Sed e awk sono processi separati, quindi non possono mai essere veloci come qualcosa che bash può fare in modo nativo senza avviare un processo separato. Normalmente questa differenza è appena percettibile, ma dove le prestazioni sono importanti negli script di shell è di solito un certo ciclo o il calcolo viene ripetuto un numero molto grande di volte e la generazione di migliaia di processi sarà notevolmente più lenta rispetto a fare una semplice manipolazione di stringhe in bash nativamente.
jw013,

2
@ jw013 Questo è vero per le stringhe brevi come "ciao mondo" dalla domanda, ma se la stringa è molto lunga, diciamo il trmanuale, allora è vero il contrario perché il tempo di generazione dei processi è trascurabile rispetto al tempo di manipolazione della stringa per cui sede awksono dedicati. Se la stringa è estremamente lunga, dite l'intero manuale di bash, allora bash può semplicemente rifiutare di procedere del tutto, a causa di alcune limitazioni interne.
Jimmij,

2
@ jw013 sto sostenendo codice di manipolazione delle stringhe che di bash è meno efficienti strumenti dedicati come allora sed, awk, tro simili. Guarda la risposta gena2x, che ho modificato qualche tempo fa aggiungendo esattamente queste informazioni: unix.stackexchange.com/questions/162221/… potresti voler confrontarlo con la risposta terdon alla stessa domanda in cui dà il tempo per stringhe brevi in ​​cui la generazione del processo del caso richiede più tempo. Puoi testarlo tu stesso e pubblicare il risultato.
Jimmij,

1
@Miati Perché pensi che questo extra read x; echo $xsia meglio per le prestazioni? La sintassi non sembra più corta o più pulita. x=${x// /_}; x=${x^^}è un modo molto più conciso di fare la stessa cosa di {read x; echo ${x.... Per quanto riguarda le prestazioni, @jimmij ha sottolineato che tr/ sedsarebbe più veloce di bash, conteggio delle forche uguale. L'uso di una pipe comporta sempre un ulteriore processo, quindi l'argomento del salvataggio di una fork non si applica più. Quindi, se stai usando pipe, usa semplicemente sed/ trecc. Se riesci a farlo in bash, fallo e salta questa read x; echo $xassurdità.
jw013,

Risposte:


9

Cosa ha detto Jimmij. Il suo ultimo esempio è il più vicino che puoi ottenere a ciò che stai tentando nella tua espressione convogliata.

Ecco una variante su quel tema:

echo 'hello world'|echo $(read s;s=${s^^};echo ${s// /_})

Sarei propenso a usare tr, poiché è abbastanza veloce.

echo 'hello world'|tr ' [:lower:]' '_[:upper:]'

Suppongo sia un peccato che bash non permetta l'espansione dei parametri nidificati; OTOH, l'uso di tali espressioni nidificate potrebbe facilmente portare a codice che è doloroso leggere. A meno che tu non abbia davvero bisogno di cose per funzionare il più velocemente possibile, è meglio scrivere codice che sia facile da leggere, comprendere e mantenere, piuttosto che un codice intelligente che sia un PITA per il debug. E se davvero fare le cose devono essere fatte a tutta velocità si dovrebbe utilizzare codice compilato, non uno script.


7

Non è possibile passare le espansioni dei parametri in questo modo. Quando ti riferisci xall'uso del $simbolo come nel "${x}"modulo, allora deve essere un nome di variabile reale, non un input standard, almeno non in bash. In zshè possibile eseguire sostituzioni di parametri nidificati nel modo seguente:

$ x=''hello world'
$ echo ${${x// /_}:u}
HELLO_WORLD

(nota: :uè zshlo stesso ^^di bash)

La nidificazione in bash non è possibile e penso che ciò che hai scritto in questione sia il migliore che si possa ottenere, ma se per qualsiasi strana ragione è necessario coinvolgere i tubi nell'equazione, allora potresti provare questo:

$ echo 'hello world' | { read x; echo "${x// /_}"; } | { read y; echo "${y^^}"; }
HELLO_WORLD

1
Considerando la nostra recente conversazione su come tr/ sedsono più veloci rispetto bashall'elaborazione delle stringhe e considerando come stai usando le pipe per passare le stringhe tramite l'I / O standard, vedo letteralmente il punto zero di fare quelle operazioni in bash invece di tr/ sed. Perché mai uno | { read x; echo $x... }al contrario di uno | sedche fa la stessa cosa?
jw013,

1
@ jw013 francamente, vedo quasi inutilmente. È solo un esempio per coinvolgere con forza le pipe al problema, perché OP ha esplicitamente chiesto loro e non voleva usare programmi esterni (entrambi echoe readsono integrati bash, quindi in linea di principio un po 'più veloce). Come ho già scritto nella risposta, la manipolazione progressiva dei parametri che OP ha nella domanda è la migliore a mio parere per questo compito in bash. Comunque il problema è piuttosto accademico.
Jimmij,
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.