Mám nějaké textové soubory a chtěl bych mít možnost přesunout libovolný řádek v libovolném souboru nahoru nebo dolů o jeden řádek (řádky na začátku nebo na konci souboru by zůstaly tam, kde jsou). Mám nějaký fungující kód, ale zdá se mi to zmatečné a nejsem přesvědčen, že mám pokryty všechny případy hran, takže by mě zajímalo, jestli existuje nějaký nástroj nebo paradigma, které to dělá lépe (např. snáze pochopit kód (pro jiné čtenáře nebo mě za 6 měsíců), snadněji se ladí a snáze se udržuje; „efektivnější“ není příliš důležité).
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
}
Mohu provádět kontrolu chyb vstupů uvnitř nebo vně těchto funkcí, ale bonusové body, pokud jsou špatné vstupy (jako necelá čísla, neexistující soubory nebo čísla řádků větší než délka souboru) zpracovány rozumně.
Chci, aby to běželo ve skriptu Bash na moderních systémech Debian/Ubuntu. Nemám vždy přístup root, ale mohu očekávat, že budou nainstalovány „standardní“ nástroje (předpokládejme sdílený webový server) a může mít možnost požádat o instalaci dalších nástrojů, pokud mohu požadavek odůvodnit (ačkoli méně externích závislostí je vždy lepší).
Příklad:
$ 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
$
Přijatá odpověď:
Podobné jako Archemar Tento návrh byste mohli naskriptovat pomocí ed
:
printf %s\n ${linenr}m${addr} w q | ed -s infile
tj.
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
např. přesunout linku č. 21
o jeden řádek výše:
printf %s\n 21m19 w q | ed -s infile
přesunout linku č. 21
o řádek níže:
printf %s\n 21m22 w q | ed -s infile
Ale protože potřebujete pouze posunout určitý řádek nahoru nebo dolů o jeden řádek, můžete také říci, že prakticky chcete prohodit dva po sobě jdoucí řádky. Seznamte se s sed
:
sed -i -n 'addr{h;n;G};p' infile
tj.
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
např. přesunout linku č. 21
o jeden řádek výše:
sed -i -n '20{h;n;G};p' infile
přesunout linku č. 21
o řádek níže:
sed -i -n '21{h;n;G};p' infile
Použil jsem gnu sed
syntaxe výše. Pokud jde o přenositelnost:
sed -n 'addr{
h
n
G
}
p' infile
Kromě toho obvyklé kontroly:soubor existuje a lze do něj zapisovat; file_length > 2
; line_no. > 1
; line_no. < file_length
;