GNU/Linux >> Linux Esercitazione >  >> Linux

Perché Printf è meglio di Echo?

Ho sentito che printf è meglio di echo . Posso ricordare solo un'istanza dalla mia esperienza in cui ho dovuto usare printf perché echo non ha funzionato per inserire del testo in qualche programma su RHEL 5.8 ma printf fatto. Ma a quanto pare ci sono altre differenze, e vorrei chiedere quali sono e se ci sono casi specifici in cui utilizzare l'una rispetto all'altra.

Risposta accettata:

Fondamentalmente, è un problema di portabilità (e affidabilità).

Inizialmente, echo non ha accettato alcuna opzione e non ha ampliato nulla. Tutto quello che stava facendo era emettere i suoi argomenti separati da uno spazio e terminati da un carattere di nuova riga.

Qualcuno ha pensato che sarebbe bello se potessimo fare cose come echo "nt" per generare caratteri di nuova riga o di tabulazione oppure avere un'opzione per non generare il carattere di nuova riga finale.

Hanno quindi riflettuto di più ma invece di aggiungere quella funzionalità alla shell (come perl dove tra virgolette doppie, t in realtà significa un carattere di tabulazione), lo hanno aggiunto a echo .

David Korn si rese conto dell'errore e introdusse una nuova forma di virgolette di shell:$'...' che è stato poi copiato da bash e zsh ma ormai era troppo tardi.

Ora quando uno standard UNIX echo riceve un argomento che contiene i due caratteri e t , invece di emetterli, restituisce un carattere di tabulazione. E non appena vede c in un argomento, interrompe l'output (quindi non viene nemmeno emesso il newline finale).

Altre shell/fornitori/versioni Unix hanno scelto di farlo in modo diverso:hanno aggiunto un -e opzione per espandere le sequenze di escape e un -n opzione per non produrre l'ultima riga finale. Alcuni hanno un -E per disabilitare le sequenze di escape, alcuni hanno -n ma non -e , l'elenco delle sequenze di escape supportate da un echo l'implementazione non è necessariamente la stessa supportata da un altro.

Sven Mascheck ha una bella pagina che mostra l'entità del problema.

Su quelle echo implementazioni che supportano le opzioni, generalmente non c'è il supporto di un -- per contrassegnare la fine delle opzioni (il echo builtin di alcune shell non Bourne lo fanno, e zsh supporta - per questo però), quindi, ad esempio, è difficile produrre "-n" con echo in molte conchiglie.

Su alcune shell come bash ¹ o ksh93 ² o yash ($ECHO_STYLE variabile), il comportamento dipende anche da come è stata compilata la shell o dall'ambiente (GNU echo Il comportamento di 's cambierà anche se $POSIXLY_CORRECT è nell'ambiente e con la versione zsh 's con il suo bsd_echo opzione, alcuni basati su pdksh con il loro posix opzione o se sono chiamati come sh o no). Quindi due bash echo s, anche dalla stessa versione di bash non è garantito che si comportino allo stesso modo.

POSIX dice:se il primo argomento è -n o qualsiasi argomento contiene barre inverse, il comportamento non è specificato . bash echo a questo proposito non è POSIX in quanto ad esempio echo -e non emette -e<newline> come richiede POSIX. La specifica UNIX è più rigorosa, proibisce -n e richiede l'espansione di alcune sequenze di escape incluso il c uno per interrompere l'output.

Queste specifiche non vengono davvero in soccorso qui dato che molte implementazioni non sono conformi. Anche alcuni certificati sistemi come macOS non sono conformi.

Per rappresentare realmente la realtà attuale, POSIX dovrebbe effettivamente dire:se il primo argomento corrisponde a ^-([eEn]*|-help|-version)$ regexp esteso o qualsiasi argomento contiene barre rovesciate (o caratteri la cui codifica contiene la codifica del carattere barra rovesciata come α in locali che utilizzano il set di caratteri BIG5), il comportamento non è specificato.

Tutto sommato, non sai cosa echo "$var" verrà prodotto a meno che tu non possa assicurarti che $var non contiene caratteri backslash e non inizia con - . La specifica POSIX in realtà ci dice di usare printf invece in quel caso.

Quindi ciò significa che non puoi usare echo per visualizzare dati non controllati. In altre parole, se stai scrivendo uno script e sta ricevendo input esterni (dall'utente come argomenti, o nomi di file dal file system...), non puoi usare echo per visualizzarlo.

Questo va bene:

echo >&2 Invalid file.

Questo non è:

echo >&2 "Invalid file: $file"

(Anche se funzionerà bene con alcuni (non conformi a UNIX) echo implementazioni come bash 's quando xpg_echo l'opzione non è stata abilitata in un modo o nell'altro come al momento della compilazione o tramite l'ambiente).

Correlati:elaborare ogni riga di output da `ping` immediatamente in pipeline?

file=$(echo "$var" | tr ' ' _) non va bene nella maggior parte delle implementazioni (eccezioni che sono yash con ECHO_STYLE=raw (con l'avvertenza che yash Le variabili 's non possono contenere sequenze arbitrarie di byte, quindi non nomi di file arbitrari) e zsh 's echo -E - "$var" ).

printf , d'altra parte è più affidabile, almeno quando è limitato all'utilizzo di base di echo .

printf '%sn' "$var"

Verrà visualizzato il contenuto di $var seguito da un carattere di nuova riga indipendentemente dal carattere che può contenere.

printf '%s' "$var"

Lo produrrà senza il carattere di nuova riga finale.

Ora, ci sono anche differenze tra printf implementazioni. C'è un nucleo di funzionalità che è specificato da POSIX, ma poi ci sono molte estensioni. Ad esempio, alcuni supportano un %q per citare gli argomenti ma il modo in cui è fatto varia da shell a shell, alcuni supportano uxxxx per i caratteri Unicode. Il comportamento varia per printf '%10sn' "$var" nelle impostazioni locali multibyte, ci sono almeno tre diversi risultati per printf %b '123'

Ma alla fine, se ti attieni al set di funzionalità POSIX di printf e non provare a farci niente di troppo fantasioso, sei fuori dai guai.

Ma ricorda che il primo argomento è il formato, quindi non dovrebbe contenere dati variabili/non controllati.

Un echo più affidabile può essere implementato usando printf , come:

echo() ( # subshell for local scope for $IFS
  IFS=" " # needed for "$*"
  printf '%sn' "$*"
)

echo_n() (
  IFS=" "
  printf %s "$*"
)

echo_e() (
  IFS=" "
  printf '%bn' "$*"
)

La sottoshell (che implica la generazione di un processo aggiuntivo nella maggior parte delle implementazioni della shell) può essere evitata usando local IFS con molte conchiglie, oppure scrivendolo come:

echo() {
  if [ "$#" -gt 0 ]; then
     printf %s "$1"
     shift
     if [ "$#" -gt 0 ]; then
       printf ' %s' "[email protected]"
     fi
  fi
  printf 'n'
}

Note

1. come bash 's echo il comportamento può essere modificato.

Con bash , in fase di esecuzione, ci sono due cose che controllano il comportamento di echo (accanto a enable -n echo o ridefinendo echo come funzione o alias):
il xpg_echo bash opzione e se bash è in modalità posix. posix la modalità può essere abilitata se bash si chiama sh o se POSIXLY_CORRECT è nell'ambiente o con il posix opzione:

Comportamento predefinito sulla maggior parte dei sistemi:

$ bash -c 'echo -n "
Linux
  1. [Linux]:diagnostica i problemi di rete utilizzando MTR, meglio di traceroute!

  2. Come scrivo caratteri non ASCII usando echo?

  3. Perché mmap() è più veloce dell'IO sequenziale?

  4. echo il carattere di nuova riga non funziona in bash

  5. L'algoritmo di copia dei file Linux (Ubuntu) è migliore di Windows 7?

8 motivi per cui Linux Mint è migliore di Ubuntu per principianti di Linux

11 motivi per cui Linux è migliore di Windows

Linux vs Mac:7 motivi per cui Linux è una scelta migliore del Mac

Perché Linux è così cattivo e Windows 11 è migliore in ogni singolo modo?

Come gestire più di 10 parametri in shell

Perché il comando sudo:bundle non è stato trovato?