Come cat << EOF >> un file contenente codice?


101

Voglio stampare il codice in un file usando cat <<EOF >>:

cat <<EOF >> brightup.sh
!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

ma quando controllo l'output del file, ottengo questo:

!/bin/bash
curr=1634
if [  -lt 4477 ]; then
   curr=406;
   echo   > /sys/class/backlight/intel_backlight/brightness;
fi

Ho provato a inserire virgolette singole, ma l'output porta anche le virgolette singole. Come posso evitare questo problema?


2
Dovresti anche aggiustare lo shebang. La prima linea deve essere letteralmente #!/bin/bashe nient'altro: #!è ciò che la rende una linea di shebang valida e ciò che viene dopo è il percorso dell'interprete.
tripleee

1
vedere man bashe cercare Here Documents. Tutti i dettagli lì.
Hong

1
Come ultimo aspetto, la sintassi moderna per la sostituzione del processo è $(command)invece di `command`. Per ottenere il contenuto di un file, Bash ha$(<file)
triplicato il

Risposte:


159

Hai solo bisogno di un minimo cambiamento; virgolette singole il delimitatore here-document dopo <<.

cat <<'EOF' >> brightup.sh

o equivalentemente backslash-escape:

cat <<\EOF >>brightup.sh

Senza citare, il here document subirà la sostituzione delle variabili, verranno valutati i backtick, ecc. Come hai scoperto.

Se è necessario espandere alcuni valori, ma non tutti, è necessario sfuggire individualmente a quelli che si desidera impedire.

cat <<EOF >>brightup.sh
#!/bin/sh
# Created on $(date # : <<-- this will be evaluated before cat;)
echo "\$HOME will not be evaluated because it is backslash-escaped"
EOF

produrrà

#!/bin/sh
# Created on Fri Feb 16 11:00:18 UTC 2018
echo "$HOME will not be evaluated because it is backslash-escaped"

Come suggerito da @fedorqui , ecco la sezione pertinente da man bash:

Qui documenti

Questo tipo di reindirizzamento indica alla shell di leggere l'input dalla sorgente corrente finché non viene visualizzata una riga contenente solo delimitatore (senza spazi finali). Tutte le righe lette fino a quel punto vengono quindi utilizzate come input standard per un comando.

Il formato di here-documents è:

      <<[-]word
              here-document
      delimiter

Nessuna espansione dei parametri, sostituzione dei comandi, espansione aritmetica o espansione del percorso viene eseguita sulla parola. Se vengono citati caratteri in una parola, il delimitatore è il risultato della rimozione delle virgolette sulla parola e le righe nel documento qui non vengono espanse. Se la parola non è quotata, tutte le righe del here-document sono soggette all'espansione dei parametri, alla sostituzione dei comandi e all'espansione aritmetica . In quest'ultimo caso, la sequenza di caratteri \ viene ignorata e \ deve essere utilizzata per citare i caratteri \, $ e `.


1
Vedo che hai contrassegnato come evitare le variabili di espansione heredoc? come duplicato di questo. Nessuna obiezione, solo che avrei incluso il riferimento ai documenti di Bash, come ho fatto nel mio (che avendo solo 13k visite ha ottenuto quasi 100 rep, quindi sembra essere abbastanza utile).
fedorqui 'SO stop harming'

@fedorqui Forse vuoi portare la tua risposta a questa domanda in realtà? Oppure possiamo cambiare il contrassegno duplicato in modo che sia il contrario; Ho solo guardato il punteggio della domanda, non molto il punteggio della risposta.
tripleee

Mmm che dire la fusione di loro, avendo questo come la questione di destinazione? La domanda duplicata ha un buon titolo, solo che è troppo lunga. Tuttavia, in qualche modo ha ricevuto molta attenzione negli ultimi tempi (ricevo alcuni voti positivi al mese ).
fedorqui 'SO stop harming'

@fedorqui mi piace il concetto di fusione ma non l'ho mai visto accadere in pratica; se capisco correttamente la situazione, le mod semplicemente non sono in grado di gestire fusioni non banali perché i problemi e le complessità superano di gran lunga i vantaggi. Quello che ho fatto una o due volte è stato eliminare una risposta utile e votata positivamente e ripubblicarla sotto una domanda diversa, anche se difficilmente posso consigliare questa soluzione alternativa.
tripleee

È difficile dire quando valga la pena farlo. L'ho fatto in un sito che modifico quando è stata pubblicata una buona domanda senza accorgermi che ce n'era un'altra buona di prima, entrambi con buone risposte. Rimuovere la mia risposta con un punteggio superiore a 90 non sembra un buon piano, poiché lascerebbe quella domanda orfana.
fedorqui 'SO stop harming'

20

Oppure, usando i tuoi marker EOF, devi citare il marker iniziale in modo che l'espansione non venga eseguita:

#-----v---v------
cat <<'EOF' >> brightup.sh
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

IHTH


1
Modificherei il tuo post, ma solo per sicurezza non dovrebbe essere #! / Bin / bash e non! / Bin / bash?
Matthew Hoggan

@ MatthewHoggan: Sì, hai ragione! Grazie per averlo capito. Lo sto aggiustando adesso.
bombardamento

16

Dovrebbe funzionare, l'ho appena testato e ha funzionato come previsto: nessuna espansione, sostituzione o cosa hai avuto.

cat <<< '
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
  curr=$((curr+406));
  echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi' > file # use overwrite mode so that you don't keep on appending the same script to that file over and over again, unless that's what you want. 

Anche l'utilizzo di quanto segue funziona.

cat <<< ' > file
 ... code ...'

Inoltre, vale la pena notare che quando si usano heredocs, come << EOF, la sostituzione e l'espansione variabile e simili avvengono. Quindi facendo qualcosa del genere:

cat << EOF > file
cd "$HOME"
echo "$PWD" # echo the current path
EOF

comporterà sempre l'espansione delle variabili $HOMEe $PWD. Quindi se la tua directory home è /home/foobare il percorso corrente è /home/foobar/bin, filesarà simile a questo:

cd "/home/foobar"
echo "/home/foobar/bin"

invece del previsto:

cd "$HOME"
echo "$PWD"

2
Una stringa qui tra virgolette singole ovviamente non può contenere virgolette singole, il che potrebbe essere un problema proibitivo. Qui i documenti sono l'unica soluzione sensata se si dispone di uno script che deve contenere virgolette singole e doppie, sebbene questo non sia ovviamente il caso del semplice esempio dell'OP. Inoltre, tangenzialmente, le here-string <<<sono disponibili solo a partire da Bash 3 e non possono essere trasferite ad altre shell.
tripleee

<<<è disponibile anche in Zsh
Alexej Magura

3
oggi ho appreso che puoi fare in modo che il nome del file segua immediatamente l'apertura di heredoc. Grazie @AlexejMagura!
chaseadamsio
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.