在脚本中看不到脚本中的变量(csh、tcsh)

variable in a script is not seen inside the script (csh, tcsh)

我想设置一个指示目录的变量,并用它来访问该目录中的文件。
所以我写了一个简单的脚本(注意我没有把#!/bin/csh放在前面)

文件测试:

set dir1=/tmp
ls -l $dir1

我做了 chmod +x test 和 运行 test 但它只显示我的当前目录,就像 $dir1 只是空的。我尝试使用 {XX} 无济于事。

而且我想在不使用 #!/usr/bin/env tcsh 或 #!/bin/csch 或 #!/bin/tcsh 的情况下执行此操作。不可能吗? (我不想开始一个新的 shell 但使用相同的别名和其他以前的环境变量等)

怎么了?我在 tcsh 中这样做。

添加

后来才知道

dir1=/tmp
ls $dir1

就是答案。

您应该将 shebang 添加到您的脚本中以强制使用 tcsh 否则将使用默认的 shell!!! 并且您的变量 definition/assignation 语法可能无法按预期工作!

将以下第一行添加到您的脚本中

#!/usr/bin/env tcsh

简短回答:根据您的评论,您需要使用 source 命令 运行 脚本(请参阅@ghoti 的评论)。

长答案:运行 shell 脚本有多种不同的方法,运行 脚本的不同含义。

  • 运行 脚本的通常首选方法是让脚本以 shebang (#!) 行开始,指定用于它的解释器 (/bin/sh、/bin/bash、等),然后将脚本放在路径中的目录中并按名称执行它,或者将脚本的路径指定为命令(请注意,./scriptname 是一个特殊情况,路径为 "right here").

    使用此方法,创建指定解释器的子进程 运行 并执行脚本。由于脚本在子进程中执行,因此其中设置的环境变量之类的东西不会影响它来自 运行 的 shell 的环境(即设置 dir1=/tmp 仅适用于脚本,不适用于完成后)。

  • 如果脚本没有 shebang,您也可以按名称或路径 运行 脚本。这也创建了一个子进程 运行ning 一些解释器(这有点胡说八道)。没有充分的理由这样做;使用 shebang。

  • 您可以通过显式调用解释器来 运行 脚本,如 sh /path/to/scriptbash /path/to/scriptcsh /path/to/script。请注意,如果脚本在当前目录中,则不需要完整路径;它的名字就足够了。这也是子进程中的脚本,但使用指定的解释器(如果有,则忽略 shebang 行)。

    如果您指定了错误的解释器(例如,将 sh 用于使用 bash-only 功能的脚本),这个有时会引起麻烦,并且只有在某些事情阻止您使用第一个选项。

  • 最后,您可以使用 source 命令 运行 脚本(例如 source /path/to/scriptsource scriptname 如果它在当前目录中)。与其他的不同,这不会创建子流程;它告诉当前 shell 从文件中执行命令。这意味着脚本最好用适合您当前 shell 的正确语法编写;如果您 source 来自 csh 或 tcsh 的 bash 或 sh 脚本,它不会顺利进行。这也意味着 shebang(如果有的话)被忽略了。

    如果您希望脚本中的环境变量定义在您 运行 中的 shell 脚本中可用,这是唯一的方法。 (好吧,除了在类似 sh 的 shells 中你可以使用 . 命令,因为它是 source 的同义词。)

    由于当脚本为 sourced 时,任何 shebang 行都会被忽略,您可以包含或不包含它。我倾向于在应该是 sourced, either giving the correct interpreter as a hint to users, or using a#!/bin/echoshebang to print a message if someone tries to run the script withoutsource`:

    的脚本中添加 shebang
    #!/bin/echo source this script from csh: source
    

    ...这有点像 hack,因为 /bin/echo 不是解释器;但它将 运行 命令 /bin/echo source this script from csh: source /path/to/script 至少将用户指向正确的方向。请注意,这不能防止有人用 shbash

  • 覆盖 shebang

顺便说一句,关于不兼容命令的其他一些注意事项:正如我所说,set 在 sh-like 和 csh-like shells 中做完全不同的事情。在 csh-like shells 中,您使用 set var=value 分配变量,但在 sh-like shells set 中设置脚本的位置参数(</code>,<code>, 等等),因此 set var=value</code> 设置为字符串 "var=value"。另外,在您的回答中,您使用了 <code>dir 命令——这根本不是一个 unix 命令,它是一个 DOS 命令;这表明您 运行 将这些脚本置于 Windows 下,在这种情况下,还有其他 class 潜伏的潜在兼容性混淆。

注意 shell 变量 环境变量之间的区别 成为传递给脚本调用的程序的环境的一部分。

在POSIX(或bash或相关)shell中,你可以使用内置的export将shell变量转换为环境变量-在命令中。

在csh/tcsh中,set命令设置了一个shell变量,而setenv命令设置了一个环境变量。设置后,您可以像使用 shell 变量一样在 shell 脚本中使用环境变量,但该变量将包含在子 shell 或其他程序的环境中你 运行.

注:

1% set foo=bar
2% echo $foo
bar
3% csh
1% echo $foo
foo: Undefined variable.
2%

1% setenv foo bar
2% echo $foo
bar
3% csh
1% echo $foo
bar
2%

在第一个示例中,set 命令创建了一个 shell 变量,该变量仅在设置它的 shell 的上下文中可见。在第二个示例中,setenv 命令创建了一个 environment 变量,该变量被传递给 subshell.

请注意,在脚本 中通过任一方法设置变量 将不允许将该变量传递回调用 shell,正如您将看到的使用POSIX shell 使用大括号的函数。对于 csh/tcsh 中的那种功能,您可能不得不依赖 alias 命令。例如,我使用以下别名:

alias go 'cd `dirname \!:*`'

这让我可以这样做:

1% alias go 'cd `dirname \!:*`'
2% go /usr/local/etc/foo.rc
3% pwd
/usr/local/etc
4%

查看 man tcshHistory substitution 部分,了解其工作原理。

如果您尝试构建一个依赖于传递回父级 shell 的信息的解决方案,请使用不同的 shell,或者在您的命令中使用带标准输出的反引号替换写,连同别名。