J'ai des fichiers texte et j'aimerais pouvoir déplacer une ligne arbitraire dans l'un des fichiers vers le haut ou vers le bas d'une ligne (les lignes au début ou à la fin du fichier resteraient là où elles sont). J'ai du code qui fonctionne mais il semble kludgy et je ne suis pas convaincu d'avoir couvert tous les cas limites, donc je me demande s'il existe un outil ou un paradigme qui le fait mieux (par exemple, plus facile à comprendre le code (pour d'autres lecteurs ou moi en 6 mois), plus facile à déboguer et plus facile à maintenir ; "plus efficace" n'est pas très important).
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
}
Je peux effectuer une vérification des erreurs des entrées à l'intérieur ou à l'extérieur de ces fonctions, mais les points bonus si une mauvaise entrée (comme des nombres non entiers, des fichiers inexistants ou des numéros de ligne supérieurs à la longueur du fichier) sont gérés sainement.
Je veux qu'il s'exécute dans un script Bash sur les systèmes Debian/Ubuntu modernes. Je n'ai pas toujours un accès root, mais je peux m'attendre à ce que des outils "standard" soient installés (pensez à un serveur Web partagé), et peut pouvoir demander l'installation d'autres outils si je peux justifier la demande (bien que moins de dépendances externes soit toujours mieux).
Exemple :
$ 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
$
Réponse acceptée :
Similaire à Archemar la suggestion de, vous pouvez écrire ceci avec ed
:
printf %s\n ${linenr}m${addr} w q | ed -s infile
c'est-à-dire
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
par exemple. pour déplacer la ligne no. 21
une ligne :
printf %s\n 21m19 w q | ed -s infile
pour déplacer la ligne no. 21
une ligne vers le bas :
printf %s\n 21m22 w q | ed -s infile
Mais comme vous n'avez qu'à déplacer une certaine ligne vers le haut ou vers le bas d'une ligne, vous pouvez également dire que vous voulez pratiquement échanger deux lignes consécutives. Rencontrez sed
:
sed -i -n 'addr{h;n;G};p' infile
c'est-à-dire
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
par exemple. pour déplacer la ligne no. 21
une ligne :
sed -i -n '20{h;n;G};p' infile
pour déplacer la ligne no. 21
une ligne vers le bas :
sed -i -n '21{h;n;G};p' infile
J'ai utilisé gnu sed
syntaxe ci-dessus. Si la portabilité est un problème :
sed -n 'addr{
h
n
G
}
p' infile
A part cela, les vérifications habituelles :le fichier existe et est accessible en écriture; file_length > 2
; line_no. > 1
; line_no. < file_length
;