Самый короткий способ обмена двумя файлами в bash

Можно ли обменять два файла в bash?

Или они могут быть заменены короче, чем это:

cp old tmp
cp curr old
cp tmp curr
rm tmp

Ответ 1

Добавьте это в свой .bashrc:

function swap()         
{
    local TMPFILE=tmp.$$
    mv "$1" $TMPFILE
    mv "$2" "$1"
    mv $TMPFILE "$2"
}

Если вы хотите обработать потенциальный сбой промежуточных операций mv, отметьте ответьте на вопрос "Бал" .

Обратите внимание, что ни этот, ни другие ответы не предоставляют атомное решение, потому что невозможно реализовать такое использование системных вызовов Linux и/или популярных файловых систем Linux. Для ядра Дарвина отметьте exchangedata syscall.

Ответ 2

$ mv old tmp && mv curr old && mv tmp curr

немного эффективнее!

Обернуто в функцию многократного использования:

function swap()         
{
    local TMPFILE=tmp.$$
    mv "$1" $TMPFILE && mv "$2" "$1" && mv $TMPFILE $2
}

Ответ 3

tmpfile=$(mktemp $(dirname "$file1")/XXXXXX)
mv "$file1" "$tmpfile"
mv "$file2" "$file1"
mv "$tmpfile" "$file2"

Ответ 4

Вы действительно хотите обменять их? я думаю, стоит отметить, что вы можете автоматически создавать резервные копии перезаписанного файла с помощью 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

Ответ 5

Объединяя лучшие ответы, я помещаю это в свой файл ~/.bashrc:

function swap()
{
  tmpfile=$(mktemp $(dirname "$1")/XXXXXX)
  mv "$1" "$tmpfile" && mv "$2" "$1" &&  mv "$tmpfile" "$2"
}

Ответ 7

Несколько закаленная версия, которая работает как для файлов, так и для каталогов:

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.

Ответ 8

Это то, что я использую в качестве команды в своей системе ($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"

Ответ 9

с использованием mv означает, что у вас есть меньше операций, нет необходимости в окончательном rm, также mv только меняет записи каталога, поэтому вы не используете дополнительное место на диске для копирования.

Temptationh тогда должен реализовать команду swap() или ее часть. Если вы действительно тщательно проверяете коды ошибок. Может быть ужасно разрушительным. Также необходимо проверить существующий файл tmp.

Ответ 10

Идея 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; -)

Ответ 11

Одна из проблем, с которыми я столкнулся при использовании любого из предложенных здесь решений: ваши имена файлов будут активированы.

Я включил использование 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

Содержимое и имена заменяются.

Ответ 12

Конечно mv вместо cp?

Ответ 13

mv old tmp
mv curr old
mv tmp curr

Ответ 14

У меня это в рабочем 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" )
}

Это намного быстрее (нет копирования, просто переименования). Это даже уродливее. И он переименует что угодно: файлы, каталоги, каналы, устройства.

Ответ 15

Вот 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