一种可能的方法是

一种可能的方法是

我是 Bash 脚本的新手。我一直在尝试编写一个脚本,用于交换用户传递给它的两个文件的文件名。

以下是照片到目前为止我的剧本的两个版本

以下是文本格式的脚本mv

#! /bin/bash
file1=$file1
file2=$file2

echo "write file name :"
read file1 file2

if [[ $file1 = $file2 ]]
then 
  cp $file1 $file1
  mv $file1 $file2 
fi

if [[ $file2 = $file1 ]]
then   
  mv $file2 $file1
fi

但我的问题是,如果我可以编写一个脚本,让用户先写下 2 个文件名,然后脚本将交换这两个文件名

交换文件名的基本内容我读过的是这样的

cp $file1 temporaryfile
mv $file1 $file2
mv $file2 temporyfile

答案1

一种可能的方法是

不久前,我专门为此目的创建了一个函数,我将其保存在我的 中.bashrc,并且可以改编成脚本。您应该利用位置参数以便用户可以在命令行上输入文件名。这是我的原始函数:

swap_files() {
    if [ $# -ne 2 ]
    then
        echo "Usage: swap_files file1 file2"
    else
        local TMPFILE=$(mktemp) 
        mv -- "$1" "$TMPFILE"
        mv -- "$2" "$1"
        mv -- "$TMPFILE" "$2"
    fi
}

您可以删除swap_files(){声明、local关键字和关闭},并将其转换为脚本 - 只需#!/bin/bash在顶部添加即可。当然,有很多事情可以改进,但在最基本的层面上,这和交换一样简单(顺便说一下,在 C 中经常教​​交换数组项,但这只是一个切题)。

#!/bin/bash
if [ $# -ne 2 ]
then
    printf "Usage: swap_files file1 file2\n" > /dev/stderr
else
    TMPFILE=$(mktemp)
    mv -- "$1" "$TMPFILE"
    mv -- "$2" "$1"
    mv -- "$TMPFILE" "$2"
fi

当然,如果文件名包含空格,请记住引用位置参数。如下所示:

swap_files 'file 1' 'file 2'

注意使用--以避免文件名开头出现问题-。更好的方法是养成使用 引用当前工作目录中文件的习惯./,特别是如果您使用 globstar *(globstar 与这个问题无关,但如果我们讨论的是开头为 的文件名,则值得一提-)。此外,./方法更易于移植,因为某些版本的mv比如在 FreeBSD 上没有--选择。


正如 terdon 在评论中所建议的,我们还可以在第一个文件的父文件夹中创建临时文件,以避免跨文件系统移动文件。

#!/bin/bash
if [ $# -ne 2 ]
then
    printf "Usage: swap_files file1 file2\n" > /dev/stderr
else
    file1_dir=${1%/*}
    # just in case there were no slashes removed, assume cwd
    if [ "$file1_dir" = "$1" ]; then
        file1_dir="."
    fi
    tmpfile=$(mktemp -p "$file1_dir" )
    mv -- "$1" "$tmpfile"
    mv -- "$2" "$1"
    mv -- "$tmpfile" "$2"
fi

你的脚本和需要改进的地方

1. 冗余变量赋值

file1=$file1
file2=$file2

这部分将$file1变量分配给...file1变量;这样做有两个问题 - 将变量分配给其自身是多余的,并且它一开始就不存在,脚本中先前没有该变量的声明。

2. 谨防使用 read 进行分词

如果您的用户尝试将带引号的项目放入您的read命令中,将会发生以下情况:

$ read file1 file2
'one potato' 'two potato'

$ echo "$file1"
'one

$ echo "$file2"
potato' 'two potato'

根据 shell 的行为,shell 会拆分读取的所有内容stdin,并尝试将每个单词放入相应的变量中,如果单词数超过变量数 - 它会尝试将所有内容推入最后一个变量。我建议您一次读取一个文件。

3. 复制到自身是错误的

你正在做的

cp $file1 $file1;

这将产生一个错误

$ cp input.txt input.txt
cp: 'input.txt' and 'input.txt' are the same file

也许你想做

cp "$file1" "$file1".tmp

或者像我一样直接使用mktemp命令。还要注意变量的引用,以防止分词。


其他有趣的方法

你知道吗cat 任何文件使用重定向来制作副本?所以使用mvcp不是唯一的方法。像这样:

$ cat ./wallpaper.jpg > wallpaper.jpg.tmp
$ cat ./screenshot.jpg > wallpaper.jpg
$ cat ./wallpaper.jpg.tmp > ./screenshot.jpg

答案2

如果获取两个文件名,您可以使用参数扩展来完成任务,或者您可以在脚本中读取它们。我的以下示例脚本使用参数扩展。您可能希望为移动选项使用临时目录,因为如果脚本中使用的文件名已经存在,则该文件将被默默覆盖。

#!/bin/bash

# creating a temporary directory to prevent overwrites if the file called 'tmpfile' exists
TMP=$(mktemp -d)

# copying the filenames into variables for later use
file1="$1"
file2="$2"

# swapping the files
# first move and rename file1 to $TMP/tempfile
mv "$1" $TMP/tempfile
# renaming file2 to file1 name
mv "$2" "$file1"
# now renaming and moving the tempfile to file2 
mv $TMP/tempfile "$file2"
# removing the temporary folder if tempfile is not existent anymore
[ -e $TMP/tempfile ] && echo "Error!" || rm -r $TMP

Bash 手册 #Shell 参数

参数是存储值的实体。它可以是名称、数字或下面列出的特殊字符之一。变量是用名称表示的参数。变量具有值和零个或多个属性。使用内建命令 declared 分配属性(请参阅 Bash Builtins 中内建命令 declared 的描述)。

如果已为参数分配了值,则表示已设置该参数。空字符串是有效值。一旦设置了变量,则只能使用 unset 内置命令取消设置。

可以通过以下形式的语句来分配变量

姓名=[价值]

如果你想从交互式对话框中读取文件名:

#!/bin/bash

# creating a temporary directory to prevent overwrites if the file called 'tmpfile' exists
TMP=$(mktemp -d)

# reading the filenames from user input into variables for later use
read -rp "Enter first filename: " file1
read -rp "Enter second filename: " file2

# swapping the files
# first move and rename file1 to $TMP/tempfile
mv "$file1" $TMP/tempfile
# renaming file2 to file1 name
mv "$file2" "$file1"
# now renaming and moving the tempfile to file2 
mv $TMP/tempfile "$file2"
# removing the temporary folder if tempfile is not existent anymore
[ -e $TMP/tempfile ] && echo "Error!" || rm -r $TMP

Bash 手册 #Bash 内置

read [-ers] [-a aname] [-d delim] [-i text] [-n nchars]
    [-N nchars] [-p prompt] [-t timeout] [-u fd] [name …]

从标准输入或文件描述符读取一行自由度 作为选项的参数提供-u,按照上面 [单词拆分][6] 中的描述拆分成单词,第一个单词被分配给第一个姓名,第二个单词到第二个姓名等等。如果单词数多于名称数,则剩余的单词及其中间的分隔符将分配给最后一个姓名。如果从输入流中读取的单词数少于名称数,则将剩余的名称赋值为空值。变量值中的字符IFS用于将行拆分为单词,使用与 shell 用于扩展的相同规则(如上文 [单词拆分][6] 中所述)。反斜杠字符 ' \' 可用于删除下一个读取字符和行延续的任何特殊含义。如果没有提供名称,则将读取的行分配给变量REPLY

read接受多个选项。在本例中,两个选项最相关,因为您想向用户提问并获取他们的输入。这些选项是:

  • -r→ 如果给出此选项,反斜杠将不充当转义字符。反斜杠被视为行的一部分。特别是,反斜杠-换行符对不能用作行延续。
  • -p prompt→ 显示迅速的,不带尾随换行符,然后尝试读取任何输入。仅当输入来自终端时才会显示提示。

虽然这不是最糟糕的情况-r,但您几乎总是希望将其包括在内,以防止它\充当转义字符。-p向用户显示提示。

相关内容