Ho alcuni file di testo e vorrei poter spostare una riga arbitraria in uno qualsiasi dei file in alto o in basso di una riga (le righe all'inizio o alla fine del file rimarrebbero dove si trovano). Ho del codice funzionante ma sembra ingannevole e non sono convinto di aver coperto tutti i casi limite, quindi mi chiedo se c'è qualche strumento o paradigma che lo fa meglio (ad esempio, più facile da capire il codice (per altri lettori o me in 6 mesi), più facile da eseguire il debug e più facile da mantenere; "più efficiente" non è molto importante).
move_up() {
# fetch line with head -<line number> | tail -1
# insert that one line higher
# delete the old line
sed -i -e "$((line_number-1))i$(head -$line_number $file | tail -1)" -e "${line_number}d" "$file"
}
move_down() {
file_length=$(wc -l < "$file")
if [[ "$line_number" -ge $((file_length - 1)) ]]; then
# sed can't insert past the end of the file, so append the line
# then delete the old line
echo $(head -$line_number "$file" | tail -1) >> "$file"
sed -i "${line_number}d" "$file"
else
# get the line, and insert it after the next line, and delete the original
sed -i -e "$((line_number+2))i$(head -$line_number $file | tail -1)" -e "${line_number}d" "$file"
fi
}
Posso eseguire il controllo degli errori degli input all'interno o all'esterno di queste funzioni, ma i punti bonus se l'input errato (come numeri interi, file inesistenti o numeri di riga maggiori della lunghezza del file) viene gestito in modo sano.
Voglio che venga eseguito in uno script Bash sui moderni sistemi Debian/Ubuntu. Non ho sempre l'accesso come root, ma posso aspettarmi che vengano installati strumenti "standard" (pensa a un server Web condiviso) e potrei essere in grado di richiedere l'installazione di altri strumenti se posso giustificare la richiesta (sebbene meno dipendenze esterne è sempre meglio).
Esempio:
$ cat b
1
2
3
4
$ file=b line_number=3 move_up
$ cat b
1
3
2
4
$ file=b line_number=3 move_down
$ cat b
1
3
4
2
$
Risposta accettata:
Simile a Archemar 's suggerimento, potresti scriverlo con ed
:
printf %s\n ${linenr}m${addr} w q | ed -s infile
cioè
linenr # is the line number
m # command that moves the line
addr=$(( linenr + 1 )) # if you move the line down
addr=$(( linenr - 2 )) # if you move the line up
w # write changes to file
q # quit editor
per esempio. per spostare la linea n. 21
una formazione:
printf %s\n 21m19 w q | ed -s infile
per spostare la linea n. 21
una riga in basso:
printf %s\n 21m22 w q | ed -s infile
Ma poiché devi solo spostare una determinata riga su o giù di una riga, potresti anche dire che vuoi praticamente scambiare due righe consecutive. Incontra sed
:
sed -i -n 'addr{h;n;G};p' infile
cioè
addr=${linenr} # if you move the line down
addr=$(( linenr - 1 )) # if you move the line up
h # replace content of the hold buffer with a copy of the pattern space
n # read a new line replacing the current line in the pattern space
G # append the content of the hold buffer to the pattern space
p # print the entire pattern space
per esempio. per spostare la linea n. 21
una formazione:
sed -i -n '20{h;n;G};p' infile
per spostare la linea n. 21
una riga in basso:
sed -i -n '21{h;n;G};p' infile
Ho usato gnu sed
sintassi sopra. Se la portabilità è un problema:
sed -n 'addr{
h
n
G
}
p' infile
A parte questo, i soliti controlli:il file esiste ed è scrivibile; file_length > 2
; line_no. > 1
; line_no. < file_length
;