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.