Можно ли обменять два файла в bash?
Или они могут быть заменены короче, чем это:
cp old tmp
cp curr old
cp tmp curr
rm tmp
Можно ли обменять два файла в bash?
Или они могут быть заменены короче, чем это:
cp old tmp
cp curr old
cp tmp curr
rm tmp
Добавьте это в свой .bashrc:
function swap()
{
local TMPFILE=tmp.$$
mv "$1" $TMPFILE
mv "$2" "$1"
mv $TMPFILE "$2"
}
Если вы хотите обработать потенциальный сбой промежуточных операций mv
, отметьте ответьте на вопрос "Бал" .
Обратите внимание, что ни этот, ни другие ответы не предоставляют атомное решение, потому что невозможно реализовать такое использование системных вызовов Linux и/или популярных файловых систем Linux. Для ядра Дарвина отметьте exchangedata
syscall.
$ mv old tmp && mv curr old && mv tmp curr
немного эффективнее!
Обернуто в функцию многократного использования:
function swap()
{
local TMPFILE=tmp.$$
mv "$1" $TMPFILE && mv "$2" "$1" && mv $TMPFILE $2
}
tmpfile=$(mktemp $(dirname "$file1")/XXXXXX)
mv "$file1" "$tmpfile"
mv "$file2" "$file1"
mv "$tmpfile" "$file2"
Вы действительно хотите обменять их? я думаю, стоит отметить, что вы можете автоматически создавать резервные копии перезаписанного файла с помощью mv:
mv new old -b
вы получите:
old and old~
если вы хотите иметь
old and old.old
вы можете использовать -S для изменения ~ вашего пользовательского суффикса
mv new old -b -S .old
ls
old old.old
используя этот подход, вы можете быстрее их обменивать, используя только 2 mv:
mv new old -b && mv old~ new
Объединяя лучшие ответы, я помещаю это в свой файл ~/.bashrc:
function swap()
{
tmpfile=$(mktemp $(dirname "$1")/XXXXXX)
mv "$1" "$tmpfile" && mv "$2" "$1" && mv "$tmpfile" "$2"
}
Вы можете просто перенести их, вместо того, чтобы делать копию.
#!/bin/sh
# Created by Wojtek Jamrozy (www.wojtekrj.net)
mv $1 cop_$1
mv $2 $1
mv cop_$1 $2
http://www.wojtekrj.net/2008/08/bash-script-to-swap-contents-of-files/
Несколько закаленная версия, которая работает как для файлов, так и для каталогов:
function swap()
{
if [ ! -z "$2" ] && [ -e "$1" ] && [ -e "$2" ] && ! [ "$1" -ef "$2" ] && (([ -f "$1" ] && [ -f "$2" ]) || ([ -d "$1" ] && [ -d "$2" ])) ; then
tmp=$(mktemp -d $(dirname "$1")/XXXXXX)
mv "$1" "$tmp" && mv "$2" "$1" && mv "$tmp"/"$1" "$2"
rmdir "$tmp"
else
echo "Usage: swap file1 file2 or swap dir1 dir2"
fi
}
Это работает в Linux. Не уверен в OS X.
Это то, что я использую в качестве команды в своей системе ($HOME/bin/swapfiles
). Я думаю, что он относительно устойчив к плохому.
#!/bin/bash
if [ "$#" -ne 2 ]; then
me=`basename $0`
echo "Syntax: $me <FILE 1> <FILE 2>"
exit -1
fi
if [ ! -f $1 ]; then
echo "File '$1' does not exist!"
fi
if [ ! -f $2 ]; then
echo "File '$2' does not exist!"
fi
if [[ ! -f $1 || ! -f $2 ]]; then
exit -1
fi
tmpfile=$(mktemp $(dirname "$1")/XXXXXX)
if [ ! -f $tmpfile ]; then
echo "Could not create temporary intermediate file!"
exit -1
fi
# move files taking into account if mv fails
mv "$1" "$tmpfile" && mv "$2" "$1" && mv "$tmpfile" "$2"
с использованием mv означает, что у вас есть меньше операций, нет необходимости в окончательном rm, также mv только меняет записи каталога, поэтому вы не используете дополнительное место на диске для копирования.
Temptationh тогда должен реализовать команду swap() или ее часть. Если вы действительно тщательно проверяете коды ошибок. Может быть ужасно разрушительным. Также необходимо проверить существующий файл tmp.
Идея Hardy для меня была достаточно хороша. Поэтому я попытался использовать следующие два файла для обмена "sendms.properties", "sendms.properties.swap". Но как только я назвал эту функцию тем же аргументом "sendms.properties", то этот файл был удален. Избегав такого рода FAIL, я добавил несколько строк: -)
function swp2file()
{ if [ $1 != $2 ] ; then
local TMPFILE=tmp.$$
mv "$1" $TMPFILE
mv "$2" "$1"
mv $TMPFILE "$2"
else
echo "swap requires 2 different filename"
fi
}
Еще раз спасибо Hardy; -)
Одна из проблем, с которыми я столкнулся при использовании любого из предложенных здесь решений: ваши имена файлов будут активированы.
Я включил использование basename
и dirname
, чтобы сохранить имена файлов неповрежденными *.
swap() {
if (( $# == 2 )); then
mv "$1" /tmp/
mv "$2" "`dirname $1`"
mv "/tmp/`basename $1`" "`dirname $2`"
else
echo "Usage: swap <file1> <file2>"
return 1
fi
}
Я тестировал это в bash и zsh.
* Итак, чтобы уточнить, как это лучше:
Если вы начинаете с:
dir1/file2: this is file2
dir2/file1: this is file1
другие решения получат:
dir1/file2: this is file1
dir2/file1: this is file2
Содержимое обменивается, но имена файлов остались. Мое решение делает это:
dir1/file1: this is file1
dir2/file2: this is file2
Содержимое и имена заменяются.
Конечно mv
вместо cp
?
mv old tmp
mv curr old
mv tmp curr
У меня это в рабочем script, который я доставил. Он написан как функция, но вы бы вызывали его
d_swap lfile rfile
В GNU mv есть -b и -T. Вы можете обращаться с каталогами, используя -T переключатель.
Кавычки предназначены для имен файлов с пробелами.
Это немного подробный, но я использовал его много раз с обоими файлами и каталогами. Могут быть случаи, когда вы захотите переименовать файл с именем, указанным в каталоге, но это не обрабатывается этой функцией.
Это не очень эффективно, если все, что вы хотите сделать, - это переименовать файлы (оставив их там, где они есть), что лучше сделать с помощью переменной оболочки.
d_swap() {
test $# -eq 2 || return 2
test -e "$1" || return 3
test -e "$2" || return 3
if [ -f "$1" -a -f "$2" ]
then
mv -b "$1" "$2" && mv "$2"~ "$1"
return 0
fi
if [ -d "$1" -a -d "$2" ]
then
mv -T -b "$1" "$2" && mv -T "$2"~ "$1"
return 0
fi
return 4
}
Эта функция будет переименовывать файлы. Он использует имя temp (оно помещает точку "." Перед именем) на всякий случай, если файлы/каталоги находятся в одном каталоге, что обычно происходит.
d_swapnames() {
test $# -eq 2 || return 2
test -e "$1" || return 3
test -e "$2" || return 3
local lname="$(basename "$1")"
local rname="$(basename "$2")"
( cd "$(dirname "$1")" && mv -T "$lname" ".${rname}" ) && \
( cd "$(dirname "$2")" && mv -T "$rname" "$lname" ) && \
( cd "$(dirname "$1")" && mv -T ".${rname}" "$rname" )
}
Это намного быстрее (нет копирования, просто переименования). Это даже уродливее. И он переименует что угодно: файлы, каталоги, каналы, устройства.
Вот swap
script с проверкой параноидальной ошибки, чтобы избежать маловероятного случая сбоя.
Script:
#!/bin/sh
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Expected 2 file arguments, abort!"
exit 1
fi
if [ ! -z "$3" ]; then
echo "Expected 2 file arguments but found a 3rd, abort!"
exit 1
fi
if [ ! -f "$1" ]; then
echo "File '$1' not found, abort!"
exit 1
fi
if [ ! -f "$2" ]; then
echo "File '$2' not found, abort!"
exit 1
fi
# avoid moving between drives
tmp=$(mktemp --tmpdir=$(dirname $1))
if [ $? -ne 0 ]; then
echo "Failed to create temp file, abort!"
exit 1
fi
# Exit on error,
mv $1 $tmp
if [ $? -ne 0 ]; then
echo "Failed to to first file '$1', abort!"
rm $tmp
exit 1
fi
mv $2 $1
if [ $? -ne 0 ]; then
echo "Failed to move first file '$2', abort!"
# restore state
mv $tmp $1
if [ $? -ne 0 ]; then
echo "Failed to move file, (unable to restore) '$1' has been left at '$tmp'!"
fi
exit 1
fi
mv $tmp $2
if [ $? -ne 0 ]; then
# this is very unlikely!
echo "Failed to move file, (unable to restore) '$1' has been left at '$tmp', '$2' as '$1'!"
exit 1
fi