Differenza di corde in Bash


110

Sto cercando di trovare un modo per determinare la differenza tra due stringhe nel mio script. Potrei farlo facilmente con diff o comm, ma non ho a che fare con i file e preferirei non esportarli in file, fare il confronto e rileggerlo.

Vedo che comm, diff, cmp consentono tutti di passare due file OPPURE un file e l'input standard - Immagino sia un bene se non voglio produrre due file ... ma fa ancora schifo.

Ho scavato in giro pensando di poter usare grep o espressioni regolari, ma immagino di no.


1
cosa vuoi fare effettivamente?

È possibile utilizzare manipolazioni di sottostringa e operazioni di test incorporate con le modifiche IFS per confrontare, ma è necessario sapere se si desidera confrontare carattere per carattere, parola per parola, riga per riga, ignorare lo spazio bianco ...
technosaurus

Risposte:


198

Usando diffo como quello che vuoi:

diff  <(echo "$string1" ) <(echo "$string2")

Domande frequenti su Greg's Bash: sostituzione del processo

o con una pipe con nome

mkfifo ./p
diff - p <<< "$string1" & echo "$string2" > p

Domande frequenti su Greg's Bash: lavorare con Named Pipes

La pipe con nome è anche nota come FIFO.

Il -da solo è per l'input standard.

<<< è una "stringa qui".

&è come ;ma lo mette in secondo piano


5
+1 per la risposta corretta. +1 per una grande spiegazione dei simboli. Inoltre, le FAQ di Greg Bash sono state spostate su: mywiki.wooledge.org I collegamenti per le pagine precedenti sono ora su mywiki.wooledge.org/ProcessSubstitution e mywiki.wooledge.org/BashFAQ/085
timemachine3030

grazie! e inoltre, questo mostrerà i descrittori di file dinamiciFUNC(){ echo "$@"; "$@"; }; FUNC diff <(echo a) <(echo b);
Aquarius Power

Lo stavo cercando per confrontare due shasum. Non sono sicuro che esista un modo più elegante per farlo, ma funziona.
fuma

Questo sembra funzionare se ci sono più righe in $ stringa1 e $ stringa2 e diff restituisce le righe che sono state aggiunte o sottratte. Cosa succede se la stringa è una singola riga e la riga e c'è qualche differenza tra le due stringhe?
alpha_989

@ alpha_989, ecco la tua risposta: L' $ diff <(echo "Here are the letters in String One.") <(echo "Here are the characters in String Two.") \n 1c1 \n < Here are the letters in String One. \n --- \n > Here are the characters in String Two. \nuso della pipe è simile, tranne che mostra un numero di processo, inizia con il 1c1dopo il successivo $e aspetta finché non premi <kbd> Invio <kbd> (oppure puoi eseguire altri comandi ...)
bballdave025

19

Mi ricorda questa domanda: come puoi diffondere due pipeline in Bash?

Se sei in una sessione bash, potresti fare:

diff <cmd1 <cmd2
diff <(foo | bar) <(baz | quux)

con la <creazione di pipe con nome anonimo - gestite da bash - in modo che vengano create e distrutte automaticamente, a differenza dei file temporanei.

Quindi, se riesci a isolare le tue due diverse stringhe come parte di un comando (grep, awk, sed, ...), puoi fare, ad esempio, qualcosa come:

diff < grep string1 myFile < grep string2 myFile

(se supponi di avere nei tuoi file righe simili string1=very_complicated_valuee a string2=another_long_and_complicated_value': senza conoscere il formato interno del tuo file, non posso consigliarti un comando preciso)


13

Preferisco cmpe la funzione di sostituzione del processo di bash:

$ cmp -bl <(echo -n abcda) <(echo -n aqcde)
  2 142 b    161 q
  5 141 a    145 e

Dicendo in posizione 2, ab si verifica per il primo, ma aq per il secondo. Nella posizione 5, sta accadendo un'altra differenza. Basta sostituire quelle stringhe con le variabili e il gioco è fatto.


Funziona solo quando le stringhe hanno la stessa lunghezza!
strpeter

11

Supponi di avere tre corde

a="this is a line"
b="this is"
c="a line"

Per rimuovere il prefisso b da a

echo ${a#"$b"}  # a line

Per rimuovere il suffisso c da a

echo ${a%"$c"}  # this is

2
Immagino che questo sia il modo spregevole di farlo. Ha funzionato bene. Tuttavia, questa sintassi è un po 'difficile da comprendere.
Mikael Roos

@MikaelRoos d'accordo. Più facile da leggere (per me comunque) sarebbe usare sed: echo "$a" | sed "s!^$b!!g" (ho sostituito il separatore sed standard / per! Nel caso in cui le variabili trattate siano percorsi. Inoltre, potresti usare una stringa qui invece di echo:. sed ... <<< $a)
ACK_stoverflow

1

Un altro esempio:

before="184613 102050 83756 63054"
after="184613 102050 84192 83756 63054"

comm -23 <(tr ' ' $'\n' <<< $after | sort) <(tr ' ' $'\n' <<< $before | sort)

Uscite

84192

Risposta originale qui

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.