احصل على الدليل المصدر لبرنامج Bash النصي من داخل البرنامج النصي نفسه

StackOverflow https://stackoverflow.com/questions/59895

  •  09-06-2019
  •  | 
  •  

سؤال

كيف يمكنني الحصول على مسار الدليل الذي يوجد به ملف سحق يقع البرنامج النصي، داخل هذا السيناريو؟

على سبيل المثال، لنفترض أنني أريد استخدام برنامج Bash النصي كمشغل لتطبيق آخر.أريد تغيير دليل العمل إلى الدليل الذي يوجد به البرنامج النصي Bash، حتى أتمكن من العمل على الملفات الموجودة في هذا الدليل، كما يلي:

$ ./application
هل كانت مفيدة؟

المحلول

#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

عبارة عن سطر واحد مفيد سيمنحك اسم الدليل الكامل للبرنامج النصي بغض النظر عن المكان الذي يتم استدعاؤه منه.

سيعمل طالما أن المكون الأخير للمسار المستخدم للعثور على البرنامج النصي ليس رابطًا رمزيًا (روابط الدليل جيدة).إذا كنت تريد أيضًا حل أي روابط إلى البرنامج النصي نفسه، فأنت بحاجة إلى حل متعدد الأسطر:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

وهذا الأخير سوف يعمل مع أي مجموعة من الأسماء المستعارة، source, bash -c, ، الروابط الرمزية، الخ.

احذر:اذا أنت cd إلى دليل مختلف قبل تشغيل هذا المقتطف، قد تكون النتيجة غير صحيحة!

أيضا، احترس من $CDPATH مسكتك, ، وتأثيرات جانبية لإخراج stderr إذا قام المستخدم بتجاوز القرص المضغوط بذكاء لإعادة توجيه الإخراج إلى stderr بدلاً من ذلك (بما في ذلك تسلسلات الهروب، كما هو الحال عند الاتصال update_terminal_cwd >&2 على ماك).إضافة >/dev/null 2>&1 في نهاية الخاص بك cd سوف يعتني الأمر بكلا الاحتمالين.

لفهم كيفية عمله، حاول تشغيل هذا النموذج المطول:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

وسوف يطبع شيئًا مثل:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

نصائح أخرى

يستخدم dirname "$0":

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

استخدام pwd لن يعمل وحده إذا لم تقم بتشغيل البرنامج النصي من الدليل الموجود فيه.

[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

يعد أمر dirname هو الأمر الأساسي، حيث يقوم ببساطة بتحليل المسار حتى اسم الملف للمتغير $0 (اسم البرنامج النصي):

dirname "$0"

ولكن كما مات ب كما هو موضح، فإن المسار الذي يتم إرجاعه يختلف اعتمادًا على كيفية استدعاء البرنامج النصي.لا يقوم pwd بالمهمة لأن ذلك يخبرك فقط بالدليل الحالي، وليس بالدليل الموجود فيه البرنامج النصي.بالإضافة إلى ذلك، إذا تم تنفيذ رابط رمزي لبرنامج نصي، فستحصل على مسار (نسبي على الأرجح) إلى حيث يوجد الارتباط، وليس البرنامج النصي الفعلي.

وقد ذكر البعض الآخر readlink الأمر، ولكن في أبسط حالاته، يمكنك استخدام:

dirname "$(readlink -f "$0")"

سوف يقوم readlink بحل مسار البرنامج النصي إلى مسار مطلق من جذر نظام الملفات.لذا، فإن أي مسارات تحتوي على نقاط مفردة أو مزدوجة و/أو علامات التلدة و/أو الروابط الرمزية سيتم تحويلها إلى مسار كامل.

وإليك نصًا يوضح كلًا من هذه، Whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

تشغيل هذا البرنامج النصي في دليل منزلي باستخدام مسار نسبي:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

مرة أخرى، ولكن باستخدام المسار الكامل للبرنامج النصي:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

الآن تغيير الدلائل:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

وأخيرًا باستخدام رابط رمزي لتنفيذ البرنامج النصي:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; 
  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

يعمل لجميع الإصدارات، بما في ذلك

  • عند الاتصال عبر رابط ناعم متعدد العمق،
  • عندما الملف عليه
  • عندما يتم استدعاء البرنامج النصي بواسطة الأمر "source" الملقب ب . عامل (نقطة).
  • عندما ar $0 تم تعديله من المتصل.
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

وبدلاً من ذلك، إذا كان البرنامج النصي bash نفسه عبارة عن ملف الارتباط الرمزي النسبي أنت يريد لمتابعته وإرجاع المسار الكامل للبرنامج النصي المرتبط:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

SCRIPT_PATH ويرد في المسار الكامل، بغض النظر عن كيفية تسميته.
فقط تأكد من تحديد موقع هذا في بداية البرنامج النصي.

هذا التعليق والكود الحقوق المتروكة، الترخيص القابل للتحديد بموجب GPL2.0 أو الأحدث أو CC-SA 3.0 (CreativeCommons Share Alike) أو الأحدث.(ج) 2008.كل الحقوق محفوظة.لا يوجد ضمان من أي نوع.لقد تم تحذيرك.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656

اجابة قصيرة:

`dirname $0`

أو (ويفضل):

$(dirname "$0")

يمكنك استخدام $BASH_SOURCE

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

لاحظ أنك بحاجة إلى استخدام #!/bin/bash وليس #!/bin/sh نظرًا لأنه امتداد bash

هذا يجب أن يفعل ذلك:

DIR=$(dirname "$(readlink -f "$0")")

يعمل مع الروابط الرمزية والمسافات في المسار.انظر صفحات الرجل ل dirname و readlink.

يحرر:

من مسار التعليق يبدو أنه لا يعمل مع نظام التشغيل Mac OS.ليس لدي فكرة لماذا هذا هو.أي اقتراحات؟

pwd يمكن استخدامها للعثور على دليل العمل الحالي، و dirname للعثور على دليل ملف معين (الأمر الذي تم تشغيله هو $0, ، لذا dirname $0 يجب أن يوفر لك دليل البرنامج النصي الحالي).

لكن، dirname يعطي بدقة جزء الدليل من اسم الملف، والذي من المرجح أن يكون مرتبطًا بدليل العمل الحالي.إذا كان البرنامج النصي الخاص بك يحتاج إلى تغيير الدليل لسبب ما، فسيتم إخراج الإخراج من dirname يصبح بلا معنى.

أقترح ما يلي:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

بهذه الطريقة، تحصل على دليل مطلق، وليس نسبي.

نظرًا لأنه سيتم تشغيل البرنامج النصي في نسخة bash منفصلة، ​​ليست هناك حاجة لاستعادة دليل العمل بعد ذلك، ولكن إذا كنت تريد التغيير مرة أخرى في البرنامج النصي الخاص بك لسبب ما، فيمكنك بسهولة تعيين قيمة pwd إلى متغير قبل تغيير الدليل، لاستخدامه في المستقبل.

على الرغم من مجرد

cd `dirname $0`

يحل السيناريو المحدد في السؤال، أجد أن وجود المسار المطلق أكثر فائدة بشكل عام.

لا أعتقد أن الأمر سهل كما تصوره الآخرون.لا يعمل pwd، لأن الدليل الحالي ليس بالضرورة هو الدليل الذي يحتوي على البرنامج النصي.$0 لا يحتوي دائمًا على المعلومات أيضًا.خذ بعين الاعتبار الطرق الثلاث التالية لاستدعاء البرنامج النصي.

./script

/usr/bin/script

script

في الطريقتين الأولى والثالثة، لا يحتوي $0 على معلومات المسار الكاملة.في الثانية والثالثة، الأشخاص ذوي الإعاقة لا تعمل.الطريقة الوحيدة للحصول على الدليل بالطريقة الثالثة هي المرور عبر المسار والعثور على الملف الذي يحتوي على المطابقة الصحيحة.في الأساس، سيتعين على الكود إعادة ما يفعله نظام التشغيل.

تتمثل إحدى الطرق للقيام بما تطلبه في ترميز البيانات الموجودة في /usr/share dir، والإشارة إليها بالمسار الكامل.لا ينبغي أن تكون البيانات موجودة في /usr/bin dir على أي حال، لذلك ربما يكون هذا هو الشيء الذي يجب فعله.

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )

يؤدي هذا إلى الحصول على دليل العمل الحالي على نظام التشغيل Mac OS X 10.6.6:

DIR=$(cd "$(dirname "$0")"; pwd)
$(dirname "$(readlink -f "$BASH_SOURCE")")

لقد سئمت من القدوم إلى هذه الصفحة مرارًا وتكرارًا لنسخ ولصق السطر الواحد في الإجابة المقبولة.المشكلة في ذلك هي أنه ليس من السهل فهمها وتذكرها.

فيما يلي برنامج نصي سهل التذكر:

DIR=$(dirname "${BASH_SOURCE[0]}")  # get the directory name
DIR=$(realpath "${DIR}")    # resolve its full path if need be

هذا خاص بنظام Linux، لكن يمكنك استخدام:

SELF=$(readlink /proc/$$/fd/255)

إليك سطرًا واحدًا متوافقًا مع POSIX:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

لقد جربت كل واحدة من هذه ولم ينجح أي منها.كان أحدهم قريبًا جدًا ولكن كان به خلل صغير أدى إلى كسره بشدة؛لقد نسوا التفاف المسار بين علامات الاقتباس.

يفترض الكثير من الأشخاص أيضًا أنك تقوم بتشغيل البرنامج النصي من الصدفة، لذا انسَ عندما تفتح برنامجًا نصيًا جديدًا فإنه يكون افتراضيًا في منزلك.

جرب هذا الدليل لمعرفة الحجم:

/var/لا أحد/فكر/حول وجود المساحات/في الدليل/الاسم/وهذا هو ملفك النصي

هذا يحصل على حق بغض النظر عن كيفية أو مكان تشغيله.

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

ولجعل الأمر مفيدًا بالفعل، إليك كيفية التغيير إلى دليل البرنامج النصي الجاري تشغيله:

cd "`dirname "$0"`"

امل ان يساعد

وإليكم الطريقة الصحيحة والبسيطة:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

توضيح:

  • ${BASH_SOURCE[0]} - المسار الكامل للبرنامج النصي.ستكون قيمة هذا صحيحة حتى عند تحديد مصدر البرنامج النصي، على سبيل المثال. source <(echo 'echo $0') مطبوعات سحق, ، أثناء استبداله بـ ${BASH_SOURCE[0]} سيتم طباعة المسار الكامل للبرنامج النصي.(بالطبع، هذا يفترض أنك موافق على الاعتماد على Bash.)

  • readlink -f - يحل بشكل متكرر أي روابط رمزية في المسار المحدد.هذا هو امتداد GNU، وغير متوفر على (على سبيل المثال) أنظمة BSD.إذا كنت تستخدم جهاز Mac، فيمكنك استخدام Homebrew لتثبيت GNU coreutils واستبدال هذا ب greadlink -f.

  • وبالطبع dirname يحصل على الدليل الأصلي للمسار.

سأستخدم شيئًا مثل هذا:

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

تمت الإشارة إلى مراجعة طفيفة للحل e-satis و3bcdnlklvc04a في إجابتهم

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}    

يجب أن يستمر هذا في العمل في جميع الحالات التي ذكروها.

يحرر:منع popd بعد فشل عملية الدفع، وذلك بفضل konsolebox

#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

$_ تجدر الإشارة إلى أنه بديل لـ $0.إذا كنت تقوم بتشغيل برنامج نصي من bash، فيمكن اختصار الإجابة المقبولة إلى:

DIR="$( dirname "$_" )"

لاحظ أن هذا يجب أن يكون البيان الأول في البرنامج النصي الخاص بك.

لقد قارنت العديد من الإجابات المقدمة، وتوصلت إلى بعض الحلول الأكثر إحكاما.يبدو أن هذه تتعامل مع جميع حالات الحافة المجنونة التي تنشأ من مجموعتك المفضلة من:

  • المسارات المطلقة أو المسارات النسبية
  • روابط الملفات والدليل الناعمة
  • الدعوة كما script, bash script, bash -c script, source script, ، أو . script
  • المسافات، وعلامات التبويب، والأسطر الجديدة، واليونيكود، وما إلى ذلك.في الدلائل و/أو اسم الملف
  • أسماء الملفات التي تبدأ بواصلة

إذا كنت تعمل بنظام التشغيل Linux، فيبدو أن استخدام proc يعد المقبض هو الحل الأفضل لتحديد المصدر الذي تم حله بالكامل للبرنامج النصي قيد التشغيل حاليًا (في جلسة تفاعلية، يشير الرابط إلى المصدر المعني) /dev/pts/X):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

يحتوي هذا على القليل من القبح، لكن الإصلاح مدمج وسهل الفهم.نحن لا نستخدم أساسيات باش فقط، لكنني موافق على ذلك لأنه readlink يبسط المهمة إلى حد كبير.ال echo X يضيف X إلى نهاية السلسلة المتغيرة بحيث لا يتم استهلاك أي مسافة بيضاء زائدة في اسم الملف، واستبدال المعلمة ${VAR%X} في نهاية السطر يتخلص من X.لأن readlink يضيف سطرًا جديدًا خاصًا به (والذي عادةً ما يتم تناوله في استبدال الأمر إن لم يكن لخدعتنا السابقة)، علينا التخلص من ذلك أيضًا.يتم تحقيق ذلك بسهولة أكبر باستخدام $'' نظام الاقتباس، والذي يتيح لنا استخدام تسلسلات الهروب مثل \n لتمثيل الأسطر الجديدة (هذه أيضًا هي الطريقة التي يمكنك بها بسهولة إنشاء أدلة وملفات ذات أسماء مخادعة).

يجب أن يغطي ما ورد أعلاه احتياجاتك لتحديد موقع البرنامج النصي قيد التشغيل حاليًا على Linux، ولكن إذا لم يكن لديك proc نظام الملفات تحت تصرفك، أو إذا كنت تحاول تحديد المسار الذي تم حله بالكامل لملف آخر، فربما تجد الكود أدناه مفيدًا.إنه مجرد تعديل طفيف من السطر الواحد أعلاه.إذا كنت تتلاعب بأسماء أدلة/ملفات غريبة، فتحقق من الإخراج باستخدام كليهما ls و readlink غنية بالمعلومات، كما ls سيتم إخراج مسارات "مبسطة"، واستبدالها ? لأشياء مثل الخطوط الجديدة.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

جرب استخدام:

real=$(realpath $(dirname $0))

بالنسبة للأنظمة التي تحتوي على رابط قراءة GNU coreutils (على سبيل المثال.لينكس):

$(readlink -f "$(dirname "$0")")

لا حاجة للاستخدام BASH_SOURCE متى $0 يحتوي على اسم ملف البرنامج النصي.

لذا...أعتقد أنني حصلت على هذا واحد.تأخرت الحفلة، ولكن أعتقد أن البعض سيقدر وجودهم هنا عندما يصادفون هذا الموضوع.يجب أن توضح التعليقات.

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi

جرب الحل المتوافق التالي:

CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"

مثل realpath أو readlink الأوامر ليست متاحة دائمًا (حسب نظام التشغيل) و ${BASH_SOURCE[0]} متاح فقط في bash shell.

بدلا من ذلك يمكنك تجربة الوظيفة التالية في باش:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

تأخذ هذه الوظيفة وسيطة واحدة.إذا كان للوسيطة مسار مطلق بالفعل، فاطبعها كما هي، وإلا فاطبع $PWD متغير + وسيطة اسم الملف (بدون ./ بادئة).

متعلق ب:

حسنًا ، إذا كان في المسار Basename & Dirname لن يقطعوه والمشي ، يكون المسار صعبًا (ماذا لو لم يكن الوالد مسارًا للتصدير!).ومع ذلك ، يجب أن يكون للقذيفة مقبض مفتوح للنص ، وفي المقبض هو رقم 255.

SELF=`readlink /proc/$$/fd/255`

تناسبني.

لتلخيص العديد من الإجابات:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

الأوامر المستعملة

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

الإخراج:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

يرىhttps://pastebin.com/J8KjxrPF

هذا يعمل في باش 3.2:

path="$( dirname "$( which "$0" )" )"

وفيما يلي مثال على استخدامه:

قل أن لديك ~/bin الدليل الذي هو في الخاص بك $PATH.لديك البرنامج النصي أ داخل هذا الدليل.هو - هي مصدرالبرنامج النصي ~/bin/lib/B.أنت تعرف مكان ارتباط البرنامج النصي المضمن بالنص الأصلي (الدليل الفرعي ليب)، ولكن ليس حيث يتعلق الأمر بالدليل الحالي للمستخدم.

يتم حل هذا عن طريق ما يلي (داخل أ):

source "$( dirname "$( which "$0" )" )/lib/B"

لا يهم مكان وجود المستخدم أو كيفية استدعاء البرنامج النصي، فهذا سيعمل دائمًا.

لم ينجح أي من هذه الأمور مع البرنامج النصي bash الذي أطلقه Finder في OS X - وانتهى بي الأمر باستخدام:

SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
sed s:.*/Volumes/:/Volumes/:`"

ليست جميلة، لكنها تنجز المهمة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top