在脚本末尾删除所有变量是一个好习惯吗?

在脚本末尾删除所有变量是一个好习惯吗?

是否可以在脚本末尾删除 shell 脚本中使用的变量?

rm -rf $abc
rm -rf $def

或者

unset $abc
unset $def

等等。

这实际上是一个好的做法吗?

答案1

这是一个很坏实践。

rm删除文件。这与变量无关。无论如何,当脚本结束并且操作系统回收 shell 的内存时,变量本身将被释放。


在简单的情况下,与其中一个变量的值同名的文件将通过此构造删除:

abc=filename
rm -f $abc  # Deletes "filename" in the current directory

情况变得更糟。如果abcdef包含文件名作为由空格分隔的单个单词(或 的任何其他字符),您将删除这些文件,并且如果出现在任何单词中,IFS则通配符也会被扩展。*

abc='hello world'
rm -f $abc  # Deletes file "hello" and "world" (leaves "hello world" alone)
abc='5 * 3'
rm -f $abc  # Deletes all files, because * is expanded (!)
def='-r /'
rm -f $def  # Really deletes *all* files this user can access

Shell 参数扩展为$var分词,其中变量的每个字符IFS将变量分为不同的参数。那么每个词都是受文件名扩展的影响,它使用*?[abc...]模式来创建文件名。这可能会变得非常糟糕,具体取决于变量中的内容。不要这样做。


无需留空或未设置以任何方式在 shell 脚本末尾添加变量。

答案2

我想知道你实际上是指 shell 变量还是临时文件。

如果您正在做的是这样的(临时文件):

tmp=$(mktemp)
something > "$tmp"
something else < "$tmp"
rm "$tmp"

当然,完成后请继续删除临时文件。虽然如果脚本在中间崩溃,文件将留在那儿,但这也并不罕见。如果您愿意,可以trap 'rm -f -- "$tmp"' EXIT在 shell 退出时删除该文件。

不过,在删除单个文件时,您可能应该使用rm -f -- "$tmp", as-f -r` 。would help in avoiding error messages if the file was e.g. removed earlier for some reason. No reason to use

(另外,请记住引用该变量,即使mktemp默认情况下会生成“不错的”文件名。)


但是,如果您这样做(变量):

read var
do something with "$var"
rm "$var"

然后你不想那样做:您没有要删除的文件,只是用户输入的内容。当 shell 退出时,shell 变量将不再存在,因此无需取消设置它们。


不过,如果您的脚本是从另一个 shell 中获取的(或者它位于 .bashrc或类似的 shell 中),那么当您的脚本结束时,shell 不会退出。在这种情况下,它可能对unset您最后使用的任何临时变量有用,不要在 shell 的剩余生命周期中保留它们的设置。

# in .bashrc
__hour=$(date +%H)
if [ "$__hour" -lt 12 ] ; then echo "Good Morning!" ; 
elif [ "$__hour" -gt 18 ] ; then echo "Good Evening!" ;
fi
unset __hour

变量名称仍可能与该脚本外部使用的某些变量发生冲突,因此命名时必须小心。

答案3

今天我刚刚被 shell 脚本的错误输出所困扰。原因是,导出的环境变量实际上可能会潜入您的 shell 脚本中并导致令人讨厌的副作用。具体方法如下:

#!/bin/sh
#
line="$line $1"
echo $line

当作为 调用时myscript.sh smeagol,已经导出的line变量会潜入。我必须unset line在脚本条目中避免污染。

答案4

在脚本末尾取消设置所有变量是一个好习惯吗?1

这取决于您的用例和意图,因为 shell 脚本可以以不同的方式运行(请参阅下面的“1. 调用 shell 脚本的方式”部分),这会影响变量是否由原始 shell 保留(调用脚本的 shell) 或不。

例如,支持和反对 ting 变量的几种情况unset

  • 坏的, 如果该脚本的重点是设置环境变量3

    话又说回来,这也取决于 shell 脚本的调用方式(参见第 1 节)。

  • 坏的, 如果该脚本旨在公开 shell 变量出于某种目的。

    例如,脚本是一个更大的“shell脚本套件”中的一个“模块”,其他shell脚本依赖于所谓的“子脚本”设置的变量。 (与前一项相同的警告适用;请参阅第 1 节。)

  • 好的,如果有人想要保护无知的用户(包括我自己)和->如果前面的几点不适用<-(很多时候他们没有)。

    注意:无知=知识较少

    例如,“未设置部分”,其中枚举了所有 shell 变量(请参阅例子)节省了我的时间和未来的痛苦;我不会每天都写 shell 脚本,尽管我在写时给自己留下了过多的注释,但我手中的肌肉记忆并不关心我运行脚本的方式会导致长时间的调试会话(再次参见下面的“1. 调用 shell 脚本的方法”部分)。另外,如果脚本预计会被其他人使用,那么即使好的文档也无济于事,因为这通常会被忽略(包括我无知的自己)。

  • (我还缺少什么?)

1. 调用shell脚本的方式

TL;DR
(ASCII 表,因为该站点不支持 Markdown 表;请参阅此答案的更好格式版本这个要点.)

|    Shell script calling     |   Preserves     | Relevant  |
|          method             | variables and   | section   |
|                             |    exports      |           |
| --------------------------- | --------------- | --------- |
| `<interpreter> <file_name>` |        no       | 1.1       |
| `.`                         |        yes      | 1.2       | (workaround in 1.2.1) 
| executable script           |        no       | 1.3       |
| `source`                    |        yes      | 1.4       | (workaround in 1.2.1)

1.1<interpreter> <file_name>

例如,如果调用脚本script.sh并且从其包含的目录中调用它:

  •     sh script.sh
  • bash script.sh
  •   zsh script.sh
  • fish script.sh

该方法将启动一个子外壳(高级 Bash 脚本指南:第 21 章. 子 shell),这是子进程中的一个新 shell,这意味着

shell 和环境变量将被丢弃脚本运行完毕后,调用脚本的 shell 不受影响。

如果脚本的重点是设置要在当前 shell 中使用的环境变量,则使用:

1.2 的点 ( .) POSIX 标准命令

相关主题:stackoverflow: shell 脚本完成后保留环境变量

.将要 ”在当前环境下执行命令”(POSIX 标准,点手册页)而不启动子外壳

shell 和环境变量将继续有效脚本运行完毕后。

如果再次运行相同的脚本,可能会导致意外的行为。例如,给出下面的脚本(我们称之为test.sh),

#!/usr/bin/env bash

# https://unix.stackexchange.com/questions/129391/passing-named-arguments-to-shell-scripts
while [ $# -gt 0 ]; do
  case "$1" in

    --option_a|-a)
      OPTION_A=$2
      ;;
    --option_b|-b)
      OPTION_B=$2
      ;;
    *)
      printf "***************************\n"
      printf "* Error: Invalid argument.*\n"
      printf "***************************\n"
      exit 1
  esac
  shift
  shift
done

# Setting default value if script is not called with `--option`
OPTION_A="${OPTION_A:-"default A"}"
OPTION_B="${OPTION_B:-"default B"}"

echo "OPTION_A: ${OPTION_A}"
echo "OPTION_B: ${OPTION_B}"

如果通过 1.1 和 1.3 节中描述的方法运行,一切都很好:

$ chmod +x test.sh
$ ./test.sh
OPTION_A: default A
OPTION_B: default B

$ bash test.sh -a 27
OPTION_A: 27
OPTION_B: default B

$ ./test.sh      
OPTION_A: default A
OPTION_B: default B

然而,使用.or source(参见下面的 1.4 节),如果希望使用变量的默认值(如果命令行选项为不是假如:

# source test.sh === . ./test.sh

$ source test.sh
OPTION_A: default A
OPTION_B: default B

$ . ./test.sh -a 27 
OPTION_A: 27
OPTION_B: default B

$ source test.sh      
OPTION_A: 27
OPTION_B: default B

1.2.1 如何使用.(和source)而不弄乱当前的shell?

使用括号(()),它们之间的命令将在一个子外壳(与1.1和1.3节中的方法相同)。

例如,( source test.sh )

相关文档和线程:

1.3 设置脚本文件的可执行位

参见上面“1.1”中的描述<interpreter> <file_name>

相关主题:unix&linux: 为什么 Bashsource不需要执行位?

例如,如果调用脚本script.sh并且从其包含的目录中调用它:

$ chmod +x script.sh

$ ./script.sh

1.4source命令

点的同义词 (.(参见“1.2点(.) POSIX 标准命令”上面的部分)。

据我所知,该同义词存在于 bash、fish 和 zsh 中,但也可能存在于其他 shell 中,但为了可移植性source更好地使用。.

相关主题:unix&linux:为什么使用点(.)作为源的别名?为什么其他命令也没有快捷方式?


脚注

[1]:重申这个问题,因为这里的答案集中于rm并且它不会取消设置变量。总结:

  • rm删除文件

  • unset

    $用于参数扩展(GNU Bash 文档,3.5.3 Shell 参数扩展) 在这种情况下2,之前不需要variable_name;否则它将尝试取消设置variable_name所指的内容:

    $ abc=27
    $ def=abc
    
    $ echo "abc=${abc}; def=${def}"
    abc=27; def=abc
    
    #       v
    $ unset $def
    #       ^
    $ echo "abc=${abc}; def=${def}"
    abc=; def=abc
    
    #       V
    $ unset  def
    #       ^
    $ echo "abc=${abc}; def=${def}"
    abc=; def=
    

    但它可能有点复杂:unix&linux: 什么是unset做什么的?

[2]:另见stackoverflow: 什么是特殊的美元符号 shell 变量?

[3]:本主题中的好主题:

相关内容