V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
Devin
V2EX  ›  Linux

如何获取 shell 脚本自身文件名?

  •  
  •   Devin · 2016-08-30 17:47:17 +08:00 · 13256 次点击
    这是一个创建于 2991 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如脚本 abc.sh ,在这个脚本里,如何获取自身文件名 abc.sh
    22 条回复    2021-03-04 20:59:14 +08:00
    bjzhou1990
        1
    bjzhou1990  
       2016-08-30 17:51:18 +08:00   ❤️ 1
    $0
    lxy42
        2
    lxy42  
       2016-08-30 17:51:24 +08:00   ❤️ 1
    echo $(basename $0)
    Devin
        3
    Devin  
    OP
       2016-08-30 17:55:26 +08:00
    @lxy42
    @bjzhou1990
    感谢,两种方法都可行
    在这边如此容易的知识点,我找了好久 QAQ
    lxy42
        4
    lxy42  
       2016-08-30 18:08:57 +08:00   ❤️ 1
    @Devin $0 是一个变量,保存了执行文件的文件名,相当于 C 语言中的 argv[0]
    fool
        5
    fool  
       2016-08-30 18:45:30 +08:00   ❤️ 1
    楼上还不够完全正确

    应该是 echo $BASH_SOURCE

    如果是 $0 的话, source abc.sh 就会出错了
    yaxin
        6
    yaxin  
       2016-08-30 18:52:10 +08:00   ❤️ 1
    还有一种情况$0 会出错,就是 cat abc.sh | bash
    hasdream
        7
    hasdream  
       2016-08-30 19:01:18 +08:00   ❤️ 1
    一般 $(basename $0) 比如脚本在 /root 目录下 脚本名为 abc.sh 全路径运行 $0 是 /root/abc.sh 当前目录运行时 ./abc.sh basename 取最后一个文件名
    wsy2220
        8
    wsy2220  
       2016-08-30 19:01:34 +08:00 via Android   ❤️ 1
    如果是符号链接,$0 也不准
    fiht
        9
    fiht  
       2016-08-30 21:02:59 +08:00   ❤️ 1
    依楼上各位所言,确实没有一种很好的方法来确定脚本自身文件名。
    其实原理很简单。
    因为维度不一样, shell 脚本运行的时候是无法直接获得比自己高一维度的信息的。
    就像虚拟机里面运行的程序不知道自己是在虚拟机里面跑一个道理
    lrz0lrz
        10
    lrz0lrz  
       2016-08-30 21:40:21 +08:00   ❤️ 6
    $ cat abc.sh
    echo $0
    echo $BASH_SOURCE
    $ ./abc.sh
    ./abc.sh
    ./abc.sh
    $ source abc.sh
    bash
    abc.sh
    $ cat abc.sh |bash
    bash

    $ ~/temp/abc.sh
    /home/xxx/temp/abc.sh
    /home/xxx/temp/abc.sh
    vus520
        11
    vus520  
       2016-08-30 22:28:05 +08:00
    @lrz0lrz
    SoloCompany
        12
    SoloCompany  
       2016-08-31 00:07:02 +08:00   ❤️ 1
    $0 是命令行里面的执行文件
    ${BASH_SOURCE[0]} 才是当前执行文件

    比如命令行执行 0.sh 然后 0.sh 里面 include 1.sh
    那么 1.sh 里面的 $0 对应的是 0.sh, ${BASH_SOURCE[0]} 才是 1.sh
    Vicer
        13
    Vicer  
       2016-08-31 01:25:29 +08:00 via Android
    echo $0| awk -F "/" { print $NF }
    没试过,不知有效不
    Vicer
        14
    Vicer  
       2016-08-31 01:28:02 +08:00 via Android   ❤️ 1
    echo $0| awk -F "/" '{ print $NF }'
    忘打引号了
    jyf007
        15
    jyf007  
       2016-08-31 06:44:37 +08:00 via Android
    冒个泡,有道理啊。我调试 shell 实在麻烦。
    clearbug
        16
    clearbug  
       2016-08-31 07:05:47 +08:00 via Android
    涨姿势。不过楼主获取自身文件名做什么?
    GPU
        17
    GPU  
       2016-08-31 08:54:02 +08:00   ❤️ 1
    realpath path/to/filename

    这个也很有用 ,我也曾经找了很久.
    wweir
        18
    wweir  
       2016-08-31 09:31:48 +08:00   ❤️ 2
    给个建议,考虑这么多蛋疼的、少见的使用情形,不如直接来个来个运维规范。
    只让 ./xxx.sh 来运行,一切不是都解决了吗?

    当然,规范这事有时很难做,但是我们不是还可以引导么?
    脚本开头统一 #!/bin/bash , 给出去就是一个带着 x 权限的没有后缀的文件。我想面对这样一个文件没多少人会敢直接 sh XXX 来瞎执行

    PS :被各路运维 sh XXX.sh 搞得实在烦了,好端端的 bash 脚本,非要拿 dash / bash posix 模式来跑,不出错才怪。
    araraloren
        19
    araraloren  
       2016-08-31 11:12:35 +08:00   ❤️ 1
    做了个测试


    getname.sh:
    #!/usr/bin/env bash

    echo `basename $0`
    echo `basename ${BASH_SOURCE}`

    getname --> getname.sh , getname 是一个软链接

    ./getname | bash ./getname | sh ./getname

    getname
    getname

    ./getname.sh | bash ./getname.sh | ./getname.sh

    getname.sh
    getname.sh

    source ./getname

    bash
    getname

    source ./getname.sh

    bash
    getname.sh
    extreme
        20
    extreme  
       2016-08-31 15:21:51 +08:00   ❤️ 1
    C 的 argv[0],哈哈哈……


    main.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    int main(void) {
    char *argv[] = {"Hi, argv[0]", "argv[1]", NULL}, *env[] = {NULL};
    if (execve("./print_argv_0", argv, env) < 0) {
    perror("execve()");
    return -1;
    }
    return 0;
    }




    print_argv_0.c:

    #define _BSD_SOURCE

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>

    int main(int argc, char *argv[]) {
    int pid = getpid();
    char buffer[100], result_buffer[100];
    printf("%s\n", argv[0]);
    sprintf(buffer, "/proc/%d/exe", pid);
    printf("%s\n", realpath(buffer, result_buffer));
    return 0;
    }


    Example output:
    user@hostname:/tmp$ /tmp/main
    Hi, argv[0]
    /tmp/print_argv_0
    ayanmw
        21
    ayanmw  
       2021-03-04 20:35:52 +08:00
    弱弱的 提一句: caller
    bash 有一个命令 caller ,可以获得 调用堆栈的 line filename
    所以 用 caller , 基本上 就 OK 了

    ```shell
    getFileName(){
    fname=$(echo $(caller)|cut -d ' ' -f 2-)
    if test -f $fname;then
    link=$(readlink $fname);
    if test -z "$link";then echo $(basename $fname);return 0;fi
    echo $(basename $(readlink $fname));
    return 0
    fi
    echo $fname
    }

    echo fileName=$(getFileName)

    ```
    ayanmw
        22
    ayanmw  
       2021-03-04 20:59:14 +08:00
    @ayanmw 又整理了一下:
    ```bash

    getFileName(){
    call=$(caller)
    if test -z "$call";then
    echo $0
    return 0
    fi
    fname=$(echo $(caller)|cut -d ' ' -f 2-)
    if test -f $fname;then
    link=$(readlink $fname);
    if test -z "$link";then
    if test -f $fname;then
    echo $(basename $fname);return 0;
    fi
    echo $fname;return 0;
    fi
    echo $(basename $(readlink $fname));
    return 0
    fi
    echo $fname
    }

    echo fileName=$(getFileName)

    ```

    函数可以在任意文件, 你只需要 `. libFunc.sh` 或者 `source libFunc.sh` 包含这个函数, 在任何地方调用
    `echo fileName=$(getFileName)` 都可以获得 你当前所在的文件.

    caller 这个 bash 内置方法, 终于解决了我多年的疑问了.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2511 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 15:50 · PVG 23:50 · LAX 07:50 · JFK 10:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.