Esiste un'alternativa al comando "sed -i" in Solaris?


11

Nel mio progetto ho l'obbligo di sostituire un testo esistente in un file come foocon un altro testo come fooofoo:

abc.txt
name
foo
foo1

Quindi ho provato:

sed -i "s/foo/fooofoo/g" abc.txt

Tuttavia ottengo questo errore:

sed: opzione illegale - i

Ho trovato nel manuale che devo usare:

sed -i\ "s/foo/fooofoo/g" abc.txt

Tuttavia, neanche questo funziona.

Ho trovato delle alternative perle awkanche una soluzione in Solaris sedsarebbe molto apprezzata.

Sto usando questa versione di bash:

GNU bash, versione 3.2.57 (1) -release (sparc-sun-solaris2.10)


Su Solaris 11 e versioni successive, utilizzare semplicemente /usr/gnu/bin/sedper ottenere il supporto -i.
alanc,

Risposte:


15

Usa ed. È disponibile sulla maggior parte delle piattaforme e può modificare i file sul posto.
Poiché sedsi basa sulla edsintassi per la sostituzione di motivi è simile:

ed -s infile <<\IN
,s/old/new/g
w
q
IN

Perché edinvece di ex, solo curioso? (So ​​che entrambi sono conformi a POSIX e ho collegato alle specifiche.;))
Wildcard

1
@Wildcard - Potrei chiedere il contrario - perché ex? Per rispondere alla tua domanda: poiché non uso mai, non ne ex ho familiarità. a proposito, ho visto configurazioni in cui edera presente ma exnon lo era (ma non il contrario) ... il più notevole è il mio laptop :)
don_crissti

Buona risposta. :) La mia risposta: da quando uso sempre Vim, conosco molto bene i excomandi. Tutti i vicomandi che puoi digitare che iniziano con due punti sono excomandi. Esistono ovviamente alcune estensioni specifiche di Vim, ma solo il set principale di comandi è incredibilmente potente e flessibile ed è abbastanza per le modifiche con script.
Wildcard

Il mio sistema Manjaro ha exe ancora no ed.
metà

8

Se non è possibile installare GNU sed, utilizzare:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

Questo utilizza il reindirizzamento per inviare l'output di sed a un file temporaneo. Se sed viene completato correttamente, questo viene sovrascritto abc.txtcon il file temporaneo.

Come si può vedere dal codice sorgente per GNU sed , questo è esattamente ciò che sed -ifa. Quindi, questo è altrettanto efficiente sed -i.

Se esiste abc.tmpgià una possibilità, è possibile che si desideri utilizzare mktempun'utilità simile per generare il nome univoco per il temporaneo.


Grazie per l'aiuto. Tuttavia esiste qualche opzione in Solaris con la quale possiamo aggiornare il file esistente senza creare un file temporaneo?
tpsaitwal,

10
Odio interromperti, ma anche sed crea un file temporaneo. git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
Jeff Schaller

1
Puoi farlo se non ti interessano i collegamenti reali - vedi il mio esempio. C'è ancora un file temporaneo, ma è nascosto (senza nome). Senza un temporaneo, dovresti usarlo ed, poiché legge l'intero file in memoria.
Toby Speight,

3

Se vuoi l'equivalente di sed -i.bak, è piuttosto semplice.

Considera questo script per GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

Possiamo semplicemente sostituire la linea contrassegnata con

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

Questo sposta il file esistente in un backup e scrive un nuovo file.

Se vogliamo l'equivalente di

sed -i -e "$script" "$dir"  # no backup

allora è leggermente più complesso. Possiamo aprire il file per la lettura come input standard, quindi scollegarlo, prima di dirigere l'output di sed per sostituirlo:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

Lo facciamo in una sub-shell, in modo che il nostro stdin originale sia ancora disponibile dopo questo. È possibile cambiare input e tornare indietro senza una subshell, ma in questo modo mi sembra più chiaro.

Nota che stiamo attenti a copiare prima, piuttosto che creare un nuovo foofile: questo è importante se il file è noto con più di un nome (cioè ha collegamenti fissi) e vuoi essere sicuro di non interrompere i collegamenti .


3

Innanzitutto, dovresti sapere che il i\comando a cui ti riferisci è per inserire una riga di testo, non per salvare il testo modificato nel file. Non esiste un modo specificato da POSIX da utilizzare sedper questo.

Quello che puoi fare è usare ex, che è specificato da POSIX :

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

Il xcomando è per "salva ed esci". Puoi anche usare wqma xè più breve.

Il %segno all'inizio del comando sostitutivo significa "Applica questo comando a ogni riga nel buffer". Puoi 1,$s/find/replace/ganche usare .


Una grande differenza tra exe sedè che sedè un editor di stream . Funziona solo in sequenza, riga per riga. exè molto più flessibile di così, e infatti puoi eseguire direttamente la modifica interattiva dei file di testo ex. È il predecessore immediato di vi.


1

Utilizzo sede nessun file temporaneo visibile :

Puoi evitare di creare un "file temporaneo" separato visibile :

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

Spiegazione

I sistemi simili a Unix non rimuovono effettivamente i contenuti del file dal disco fino a quando non sono entrambi scollegati nel filesystem e non si aprono in alcun processo. Quindi puoi fare exec 3<per aprire il file nella shell per leggere sul descrittore di file 3, rmil file (che lo scollega dal file system) e quindi chiamare sedcon il descrittore di file 3 usato come input.

Si noti che questo è molto diverso da questo:

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

La differenza è che quando lo fai in un solo comando, la shell apre lo stesso file sia per la lettura, sia per la scrittura con l'opzione di troncare il file - poiché è sempre lo stesso file, perdi il contenuto. Ma se lo apri per la lettura, quindi rm, quindi apri lo stesso percorso per la scrittura, stai effettivamente creando un nuovo file con lo stesso percorso (ma in un nuovo inode e posizione del disco, poiché l'originale è ancora aperto): così i contenuti sono ancora disponibili.

Quindi, una volta terminato, puoi chiudere il descrittore di file che hai aperto in precedenza (è quello che fa la exec 3<&-sintassi speciale), che rilascia il file originale in modo che il sistema operativo possa eliminare (contrassegnare come inutilizzato) il suo spazio su disco.

Avvertenze

Ci sono alcune cose da tenere a mente su questa soluzione:

  1. Si ottiene solo un "passaggio" tra i contenuti - non esiste un modo portatile per una shell di "cercare" indietro nel descrittore di file - quindi una volta che un programma legge alcuni dei contenuti, altri programmi vedranno solo il resto del file. E sedleggerà l'intero file.

  2. C'è una piccola possibilità che il tuo file originale vada perso se la tua shell / script / sed viene uccisa prima che sia fatta.


Per essere chiari, @TobySpeight ha già pubblicato un esempio di questa soluzione alla fine della sua risposta, ma ho pensato che potesse usare un'elaborazione più approfondita.
mtraceur,

Per il downvoter: Anche se sono sicuro che lasciare un downvote ti fa sentire bene con te stesso, apprezzerei se tu contribuissi di più fornendo un feedback su quali problemi hai con questa risposta, come può essere migliorato, ecc. .
mtraceur

forse puoi usare perl -i
c4f4t0r

@ c4f4t0r: ci scusiamo per la risposta tardiva: sì, perl -iè un'altra buona opzione. La domanda espressamente richiesto una soluzione compatibile con Solaris' sed, a differenza di soluzioni con perle awkche hanno menzionato già trovare. Inoltre, la mia risposta aveva principalmente lo scopo di aggiungere qualcosa che pensavo fosse utile sapere e che mancava in una qualsiasi delle altre risposte.
mtraceur,
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.