Advertisement
v1ral_ITS

more noytes

Mar 30th, 2019
170
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 38.42 KB | None | 0 0
  1. None of the current solutions work if there are any newlines at the end of the directory name - They will be stripped by the command substitution. To work around this you can append a non-newline character inside the command substitution - DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)" - and remove it without a command substitution - DIR="${DIR%x}". – l0b0 Sep 24 '12 at 12:15
  2. 73
  3. @jpmc26 There are two very common situations: Accidents and sabotage. A script shouldn't fail in unpredictable ways just because someone, somewhere, did a mkdir $'\n'. – l0b0 Mar 28 '13 at 8:14
  4. 14
  5. anyone who lets people sabotage their system in that way shouldn't leave it up to bash to detect such problems... much less hire people capable of making that kind of mistake. I have never had, in the 25 years of using bash, seen this kind of thing happen anywhere.... this is why we have things like perl and practices such as taint checking (i will probably be flamed for saying that :) – osirisgothra Feb 5 '15 at 0:12
  6. 2
  7. @l0b0 Consider that you'd need the same protection on dirname, and that the directory could start with a - (e.g. --help). DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}. Perhaps this is overkill? – Score_Under Apr 28 '15 at 17:46
  8. 43
  9. I stronly suggest to read this Bash FAQ about the subject. – Rany Albeg Wein Jan 30 '16 at 2:22
  10. show 14 more comments
  11. 55 Answers
  12. active oldest votes
  13. 1 2 next
  14.  
  15. 5731
  16.  
  17. #!/bin/bash
  18.  
  19. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
  20. is a useful one-liner which will give you the full directory name of the script no matter where it is being called from.
  21.  
  22. It will work as long as the last component of the path used to find the script is not a symlink (directory links are OK). If you also want to resolve any links to the script itself, you need a multi-line solution:
  23.  
  24. #!/bin/bash
  25.  
  26. SOURCE="${BASH_SOURCE[0]}"
  27. while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  28.  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  29.  SOURCE="$(readlink "$SOURCE")"
  30.  [[ $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
  31. done
  32. DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  33. This last one will work with any combination of aliases, source, bash -c, symlinks, etc.
  34.  
  35. Beware: if you cd to a different directory before running this snippet, the result may be incorrect!
  36.  
  37. Also, watch out for $CDPATH gotchas, and stderr output side effects if the user has smartly overridden cd to redirect output to stderr instead (including escape sequences, such as when calling update_terminal_cwd >&2 on Mac). Adding >/dev/null 2>&1 at the end of your cd command will take care of both possibilities.
  38.  
  39. To understand how it works, try running this more verbose form:
  40.  
  41. #!/bin/bash
  42.  
  43. SOURCE="${BASH_SOURCE[0]}"
  44. while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  45.  TARGET="$(readlink "$SOURCE")"
  46.  if [[ $TARGET == /* ]]; then
  47.    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
  48.    SOURCE="$TARGET"
  49.  else
  50.    DIR="$( dirname "$SOURCE" )"
  51.    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
  52.    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  53.  fi
  54. done
  55. echo "SOURCE is '$SOURCE'"
  56. RDIR="$( dirname "$SOURCE" )"
  57. DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  58. if [ "$DIR" != "$RDIR" ]; then
  59.  echo "DIR '$RDIR' resolves to '$DIR'"
  60. fi
  61. echo "DIR is '$DIR'"
  62. And it will print something like:
  63.  
  64. SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
  65. SOURCE is './sym2/scriptdir.sh'
  66. DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
  67. DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
  68. shareimprove this answer
  69. edited Dec 24 '18 at 7:25
  70. community wiki
  71. 24 revs, 18 users 34%
  72. Dave Dopson
  73. 22
  74. You can fuse this approach with the answer by user25866 to arrive at a solution that works with source <script> and bash <script>: DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)". – Dan Moulding Oct 19 '11 at 15:54
  75. 18
  76. Sometimes cd prints something to STDOUT! E.g., if your $CDPATH has .. To cover this case, use DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )" – user716468 Feb 3 '13 at 2:33
  77. 86
  78. Wait, so what is the final command to use? – Xeoncross Jun 5 '14 at 14:19
  79. 137
  80. This accepted answer is not ok, it doesn't work with symlinks and is overly complex. dirname $(readlink -f $0) is the right command. See gist.github.com/tvlooy/cbfbdb111a4ebad8b93e for a testcase – tvlooy Jun 9 '15 at 19:32
  81. 118
  82. @tvlooy IMO your answer isn't exactly OK as-is either, because it fails when there is a space in the path. In contrast to a newline character, this isn't unlikely or even uncommon. dirname "$(readlink -f "$0")" doesn't add complexity and is fair measure more robust for the minimal amount of trouble. – Adrian Günter Oct 28 '15 at 23:38
  83. show 57 more comments
  84.  
  85. 742
  86.  
  87. Use dirname "$0":
  88.  
  89. #!/bin/bash
  90. echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
  91. echo "The present working directory is `pwd`"
  92. using pwd alone will not work if you are not running the script from the directory it is contained in.
  93.  
  94. [matt@server1 ~]$ pwd
  95. /home/matt
  96. [matt@server1 ~]$ ./test2.sh
  97. The script you are running has basename test2.sh, dirname .
  98. The present working directory is /home/matt
  99. [matt@server1 ~]$ cd /tmp
  100. [matt@server1 tmp]$ ~/test2.sh
  101. The script you are running has basename test2.sh, dirname /home/matt
  102. The present working directory is /tmp
  103. shareimprove this answer
  104. edited Mar 29 '18 at 22:51
  105. community wiki
  106. 4 revs, 4 users 77%
  107. matt b
  108. 23
  109. For portability beyond bash, $0 may not always be enough. You may need to substitute "type -p $0" to make this work if the command was found on the path. – Darron Oct 23 '08 at 20:15
  110. 7
  111. @Darron: you can only use type -p if the script is executable. This can also open a subtle hole if the script is executed using bash test2.sh and there is another script with the same name executable somewhere else. – D.Shawley Feb 5 '10 at 12:18
  112. 74
  113. @Darron: but since the question is tagged bash and the hash-bang line explicitly mentions /bin/bash I'd say it's pretty safe to depend on bashisms. – Joachim Sauer Jun 11 '10 at 12:56
  114. 21
  115. +1, but the problem with using dirname $0 is that if the directory is the current directory, you'll get .. That's fine unless you're going to change directories in the script and expect to use the path you got from dirname $0 as though it were absolute. To get the absolute path: pushd `dirname $0` > /dev/null, SCRIPTPATH=`pwd`, popd > /dev/null: pastie.org/1489386 (But surely there's a better way to expand that path?) – T.J. Crowder Jan 23 '11 at 10:30
  116. 6
  117. @T.J. Crowder I'm not sure sure dirname $0 is a problem if you assign it to a variable and then use it to launch a script like $dir/script.sh; I would imagine this is the use case for this type of thing 90% of the time. ./script.sh would work fine. – matt b Jan 24 '11 at 12:55
  118. show 11 more comments
  119.  
  120. 410
  121.  
  122. The dirname command is the most basic, simply parsing the path up to the filename off of the $0 (script name) variable:
  123.  
  124. dirname "$0"
  125. But, as matt b pointed out, the path returned is different depending on how the script is called. pwd doesn't do the job because that only tells you what the current directory is, not what directory the script resides in. Additionally, if a symbolic link to a script is executed, you're going to get a (probably relative) path to where the link resides, not the actual script.
  126.  
  127. Some others have mentioned the readlink command, but at its simplest, you can use:
  128.  
  129. dirname "$(readlink -f "$0")"
  130. readlink will resolve the script path to an absolute path from the root of the filesystem. So, any paths containing single or double dots, tildes and/or symbolic links will be resolved to a full path.
  131.  
  132. Here's a script demonstrating each of these, whatdir.sh:
  133.  
  134. #!/bin/bash
  135. echo "pwd: `pwd`"
  136. echo "\$0: $0"
  137. echo "basename: `basename $0`"
  138. echo "dirname: `dirname $0`"
  139. echo "dirname/readlink: $(dirname $(readlink -f $0))"
  140. Running this script in my home dir, using a relative path:
  141.  
  142. >>>$ ./whatdir.sh
  143. pwd: /Users/phatblat
  144. $0: ./whatdir.sh
  145. basename: whatdir.sh
  146. dirname: .
  147. dirname/readlink: /Users/phatblat
  148. Again, but using the full path to the script:
  149.  
  150. >>>$ /Users/phatblat/whatdir.sh
  151. pwd: /Users/phatblat
  152. $0: /Users/phatblat/whatdir.sh
  153. basename: whatdir.sh
  154. dirname: /Users/phatblat
  155. dirname/readlink: /Users/phatblat
  156. Now changing directories:
  157.  
  158. >>>$ cd /tmp
  159. >>>$ ~/whatdir.sh
  160. pwd: /tmp
  161. $0: /Users/phatblat/whatdir.sh
  162. basename: whatdir.sh
  163. dirname: /Users/phatblat
  164. dirname/readlink: /Users/phatblat
  165. And finally using a symbolic link to execute the script:
  166.  
  167. >>>$ ln -s ~/whatdir.sh whatdirlink.sh
  168. >>>$ ./whatdirlink.sh
  169. pwd: /tmp
  170. $0: ./whatdirlink.sh
  171. basename: whatdirlink.sh
  172. dirname: .
  173. dirname/readlink: /Users/phatblat
  174. shareimprove this answer
  175. edited Jan 4 '17 at 16:33
  176. community wiki
  177. 5 revs, 4 users 91%
  178. phatblat
  179. 9
  180. readlink will not availabe in some platform in default installation. Try to avoid using it if you can – T.L Jan 11 '12 at 9:14
  181. 30
  182. be careful to quote everything to avoid whitespace issues: export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" – Catskul Sep 17 '13 at 19:40
  183. 9
  184. In OSX Yosemite 10.10.1 -f is not recognised as an option to readlink. Using stat -f instead does the job. Thanks – cucu8 Nov 26 '14 at 9:29
  185. 5
  186. In OSX, there is greadlink, which is basically the readlink we are all familiar. Here is a platform independent version: dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}` – robert Jan 14 '16 at 20:16
  187. 4
  188. Good call, @robert. FYI, greadlink can easily be installed through homebrew: brew install coreutils – phatblat Jan 15 '16 at 21:27
  189. show 4 more comments
  190.  
  191. 174
  192.  
  193. pushd . > /dev/null
  194. SCRIPT_PATH="${BASH_SOURCE[0]}"
  195. if ([ -h "${SCRIPT_PATH}" ]); then
  196.  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`;
  197.  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
  198. fi
  199. cd `dirname ${SCRIPT_PATH}` > /dev/null
  200. SCRIPT_PATH=`pwd`;
  201. popd  > /dev/null
  202. Works for all versions,including
  203.  
  204. when called via multple depth soft link,
  205. when the file it
  206. when script called by command "source" aka . (dot) operator.
  207. when arg $0 is modified from caller.
  208. "./script"
  209. "/full/path/to/script"
  210. "/some/path/../../another/path/script"
  211. "./some/folder/script"
  212. Alternatively, if the bash script itself is a relative symlink you want to follow it and return the full path of the linked-to script:
  213.  
  214. pushd . > /dev/null
  215. SCRIPT_PATH="${BASH_SOURCE[0]}";
  216. if ([ -h "${SCRIPT_PATH}" ]) then
  217.  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
  218. fi
  219. cd `dirname ${SCRIPT_PATH}` > /dev/null
  220. SCRIPT_PATH=`pwd`;
  221. popd  > /dev/null
  222. SCRIPT_PATH is given in full path, no matter how it is called.
  223. Just make sure you locate this at start of the script.
  224.  
  225. This comment and code Copyleft, selectable license under the GPL2.0 or later or CC-SA 3.0 (CreativeCommons Share Alike) or later. (c) 2008. All rights reserved. No warranty of any kind. You have been warned.
  226. http://www.gnu.org/licenses/gpl-2.0.txt
  227. http://creativecommons.org/licenses/by-sa/3.0/
  228. 18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656
  229.  
  230. shareimprove this answer
  231. edited May 17 '18 at 11:21
  232. community wiki
  233. 9 revs, 7 users 54%
  234. user25866
  235. 4
  236. Nice! Could be made shorter replacing "pushd[...] popd /dev/null" by SCRIPT_PATH=readlink -f $(dirname "${VIRTUAL_ENV}"); – e-satis Nov 29 '09 at 11:34
  237. 5
  238. And instead of using pushd ...; would not it be better to use $(cd dirname "${SCRIPT_PATH}" && pwd)? But anyway great script! – ovanes Aug 18 '10 at 10:16
  239. 9
  240. Isn't the if redundant? while is testing the same thing... – gatopeich Aug 5 '11 at 13:28
  241. 4
  242. It's dangerous for a script to cd out of its current directory in the hope of cding back again later: The script may not have permission to change directory back to the directory that was current when it was invoked. (Same goes for pushd/popd) – Adrian Pronk Nov 6 '12 at 1:15
  243. 6
  244. readlink -f is GNU-specific. BSD readlink does not have that option. – bren brightwell Jun 3 '14 at 16:48
  245. show 12 more comments
  246.  
  247. 100
  248.  
  249. Short answer:
  250.  
  251. `dirname $0`
  252. or (preferably):
  253.  
  254. $(dirname "$0")
  255. shareimprove this answer
  256. edited Apr 19 '17 at 6:59
  257. community wiki
  258. 7 revs, 7 users 37%
  259. Fabien
  260. 13
  261. It won't work if you source the script. "source my/script.sh" – Arunprasad Rajkumar Feb 5 '14 at 7:34
  262. 5
  263. then nothing will – vidstige Mar 3 '16 at 10:14
  264. I use this all the time in my bash scripts that automate stuff and often invoke other scripts in the same dir. I'd never use source on these and cd $(dirname $0) is easy to remember. – guaka Jan 3 '17 at 16:28
  265. works for /bin/sh – shadi May 29 '17 at 6:25
  266. 12
  267. @vidstige: ${BASH_SOURCE[0]} instead of $0 will work with source my/script.sh – Timothy Jones Sep 27 '17 at 6:48
  268. show 1 more comment
  269.  
  270. 96
  271.  
  272. You can use $BASH_SOURCE
  273.  
  274. #!/bin/bash
  275.  
  276. scriptdir=`dirname "$BASH_SOURCE"`
  277. Note that you need to use #!/bin/bash and not #!/bin/sh since its a bash extension
  278.  
  279. shareimprove this answer
  280. edited Jun 17 '14 at 10:12
  281. community wiki
  282. 2 revs
  283. Mr Shark
  284. 9
  285. When I do ./foo/script, then $(dirname $BASH_SOURCE) is ./foo. – Till Oct 25 '10 at 17:06
  286. also works with source / . operator! – grosser Aug 11 '11 at 20:53
  287. add a comment
  288.  
  289. 61
  290.  
  291. This should do it:
  292.  
  293. DIR=$(dirname "$(readlink -f "$0")")
  294. Works with symlinks and spaces in path. See man pages for dirname and readlink.
  295.  
  296. Edit:
  297.  
  298. From the comment track it seems not to work with Mac OS. I have no idea why that is. Any suggestions?
  299.  
  300. shareimprove this answer
  301. edited Jul 29 '17 at 22:04
  302. community wiki
  303. 7 revs, 2 users 97%
  304. Simon Rigét
  305. 5
  306. with your solution, invoking the script like ./script.sh shows . instead of the full directory path – Bruno Negrão Zica Jun 14 '16 at 18:27
  307. 3
  308. There's no -f option for readlink on MacOS. Use stat instead. But still, it shows . if you are in 'this' dir. – Denis The Menace Nov 21 '16 at 9:55
  309. 1
  310. This is the best solution on Ubuntu 16.04 – fviktor Mar 19 '17 at 15:50
  311. 2
  312. But not on mac bash! – Yordan Georgiev Jul 28 '17 at 20:06
  313. add a comment
  314.  
  315. 55
  316.  
  317. pwd can be used to find the current working directory, and dirname to find the directory of a particular file (command that was run, is $0, so dirname $0 should give you the directory of the current script).
  318.  
  319. However, dirname gives precisely the directory portion of the filename, which more likely than not is going to be relative to the current working directory. If your script needs to change directory for some reason, then the output from dirname becomes meaningless.
  320.  
  321. I suggest the following:
  322.  
  323. #!/bin/bash
  324.  
  325. reldir=`dirname $0`
  326. cd $reldir
  327. directory=`pwd`
  328.  
  329. echo "Directory is $directory"
  330. This way, you get an absolute, rather then relative directory.
  331.  
  332. Since the script will be run in a separate bash instance, there is no need to restore the working directory afterwards, but if you do want to change back in your script for some reason, you can easily assign the value of pwd to a variable before you change directory, for future use.
  333.  
  334. Although just
  335.  
  336. cd `dirname $0`
  337. solves the specific scenario in the question, I find having the absolute path to more more useful generally.
  338.  
  339. shareimprove this answer
  340. edited Apr 1 '16 at 15:23
  341. community wiki
  342. 3 revs, 2 users 94%
  343. SpoonMeiser
  344. 8
  345. You can do it all in one line like this: DIRECTORY=$(cd dirname $0 && pwd) – dogbane Oct 29 '08 at 8:38
  346. This doesn't work if the script sources another script and you want to know the name of the latter. – reinierpost Mar 28 '14 at 13:10
  347. add a comment
  348.  
  349. 33
  350.  
  351. I don't think this is as easy as others have made it out to be. pwd doesn't work, as the current dir is not necessarily the directory with the script. $0 doesn't always have the info either. Consider the following three ways to invoke a script.
  352.  
  353. ./script
  354.  
  355. /usr/bin/script
  356.  
  357. script
  358. In the first and third ways $0 doesn't have the full path info. In the second and third, pwd do not work. The only way to get the dir in the third way would be to run through the path and find the file with the correct match. Basically the code would have to redo what the OS does.
  359.  
  360. One way to do what you are asking would be to just hardcode the data in the /usr/share dir, and reference it by full path. Data shoudn't be in the /usr/bin dir anyway, so this is probably the thing to do.
  361.  
  362. shareimprove this answer
  363. edited Dec 2 '11 at 14:56
  364. community wiki
  365. 2 revs, 2 users 83%
  366. Jim
  367. 9
  368. If you intend to disprove his comment, PROVE that a script CAN access where it's stored with a code example. – Richard Duerr Nov 18 '15 at 18:54
  369. add a comment
  370.  
  371. 32
  372.  
  373. SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
  374. shareimprove this answer
  375. edited May 30 '13 at 11:28
  376. community wiki
  377. 2 revs, 2 users 67%
  378. P M
  379. This is way shorter than the chosen answer. And appears to work just as well. This deserves 1000 votes just so people do not overlook it. – Patrick Sep 19 '13 at 3:07
  380. 1
  381. As many of the previous answers explain in detail, neither $0 nor pwd are guaranteed to have the right information, depending on how the script is invoked. – IMSoP Sep 23 '13 at 16:51
  382. add a comment
  383.  
  384. 28
  385.  
  386. This gets the current working directory on Mac OS X 10.6.6:
  387.  
  388. DIR=$(cd "$(dirname "$0")"; pwd)
  389. shareimprove this answer
  390. edited Dec 3 '12 at 13:29
  391. community wiki
  392. 2 revs, 2 users 89%
  393. Pubguy
  394. add a comment
  395.  
  396. 27
  397.  
  398. $(dirname "$(readlink -f "$BASH_SOURCE")")
  399. shareimprove this answer
  400. edited May 13 '18 at 4:36
  401. community wiki
  402. 3 revs, 3 users 55%
  403. test11
  404. add a comment
  405.  
  406. 26
  407.  
  408. This is Linux specific, but you could use:
  409.  
  410. SELF=$(readlink /proc/$$/fd/255)
  411. shareimprove this answer
  412. edited Dec 22 '13 at 0:07
  413. community wiki
  414. 3 revs, 3 users 75%
  415. Steve Baker
  416. 1
  417. It's also bash specific, but perhaps bash's behavior has changed? /proc/fd/$$/255 seems to point to the tty, not to a directory. For example, in my current login shell, file descriptors 0, 1, 2, and 255 all refer to /dev/pts/4. In any case, the bash manual doesn't mention fd 255, so it's probably unwise to depend on this behavior.\ – Keith Thompson Mar 29 '15 at 0:17
  418. 2
  419. Interactive shell != script. Anyway realpath ${BASH_SOURCE[0]}; would seem to be the best way to go. – Steve Baker Apr 6 '15 at 12:52
  420. add a comment
  421.  
  422. 21
  423.  
  424. Here is a POSIX compliant one-liner:
  425.  
  426. SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`
  427.  
  428. # test
  429. echo $SCRIPT_PATH
  430. shareimprove this answer
  431. edited Apr 15 '13 at 7:28
  432. community wiki
  433. 2 revs, 2 users 91%
  434. lamawithonel
  435. 3
  436. I had success with this when running a script by itself or by using sudo, but not when calling source ./script.sh – Michael R Apr 17 '13 at 21:57
  437. And it fails when cd is configured to print the new path name. – Aaron Digulla Nov 4 '13 at 13:45
  438. add a comment
  439.  
  440. 16
  441.  
  442. I tried every one of these and none of them worked. One was very close but had a tiny bug that broke it badly; they forgot to wrap the path in quotation marks.
  443.  
  444. Also a lot of people assume you're running the script from a shell so forget when you open a new script it defaults to your home.
  445.  
  446. Try this directory on for size:
  447.  
  448. /var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text
  449.  
  450. This gets it right regardless how or where you run it.
  451.  
  452. #!/bin/bash
  453. echo "pwd: `pwd`"
  454. echo "\$0: $0"
  455. echo "basename: `basename "$0"`"
  456. echo "dirname: `dirname "$0"`"
  457. So to make it actually useful here's how to change to the directory of the running script:
  458.  
  459. cd "`dirname "$0"`"
  460. Hope that helps
  461.  
  462. shareimprove this answer
  463. edited Feb 6 '11 at 2:04
  464. community wiki
  465. 4 revs
  466. Mike Bethany
  467. 3
  468. Doesn't work if the script is being sourced from another script. – reinierpost Mar 28 '14 at 13:12
  469. add a comment
  470.  
  471. 16
  472.  
  473. Here is the simple, correct way:
  474.  
  475. actual_path=$(readlink -f "${BASH_SOURCE[0]}")
  476. script_dir=$(dirname "$actual_path")
  477. Explanation:
  478.  
  479. ${BASH_SOURCE[0]} - the full path to the script. The value of this will be correct even when the script is being sourced, e.g. source <(echo 'echo $0') prints bash, while replacing it with ${BASH_SOURCE[0]} will print the full path of the script. (Of course, this assumes you're OK taking a dependency on Bash.)
  480.  
  481. readlink -f - Recursively resolves any symlinks in the specified path. This is a GNU extension, and not available on (for example) BSD systems. If you're running a Mac, you can use Homebrew to install GNU coreutils and supplant this with greadlink -f.
  482.  
  483. And of course dirname gets the parent directory of the path.
  484.  
  485. shareimprove this answer
  486. edited Feb 20 '16 at 7:53
  487. community wiki
  488. 2 revs
  489. James Ko
  490. SCRIPT_DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}")) – isapir Oct 31 '18 at 18:19
  491. add a comment
  492.  
  493. 16
  494.  
  495. I am tired of coming to this page over and over to copy paste the one-liner in the accepted answer. The problem with that is it is not easy to understand and remember.
  496.  
  497. Here is an easy-to-remember script:
  498.  
  499. DIR=$(dirname "${BASH_SOURCE[0]}")  # get the directory name
  500. DIR=$(realpath "${DIR}")    # resolve its full path if need be
  501. shareimprove this answer
  502. answered Nov 7 '18 at 4:30
  503. community wiki
  504. Thamme Gowda
  505. realpath comes from GNU coreutils, but does anyone know how common it is across other dists? – conny Mar 15 at 9:09
  506. add a comment
  507.  
  508. 15
  509.  
  510. I would use something like this:
  511.  
  512. # retrieve the full pathname of the called script
  513. scriptPath=$(which $0)
  514.  
  515. # check whether the path is a link or not
  516. if [ -L $scriptPath ]; then
  517.  
  518.     # it is a link then retrieve the target path and get the directory name
  519.     sourceDir=$(dirname $(readlink -f $scriptPath))
  520.  
  521. else
  522.  
  523.     # otherwise just get the directory name of the script path
  524.     sourceDir=$(dirname $scriptPath)
  525.  
  526. fi
  527. shareimprove this answer
  528. answered Nov 21 '12 at 3:57
  529. community wiki
  530. Nicolas
  531. This is the real one! Works with simple sh too! Problem with simple dirname "$0" based solutions: If the script is in the $PATH and is invoked without path, they will give wrong result. – Notinlist Nov 18 '14 at 10:25
  532. @Notinlist Not so. If the script is found via the PATH, $0 will contain the absolute filename. If the script is invoked with a relative or absolute filename containing a /, $0 will contain that. – Neil Mayhew Feb 3 '16 at 22:08
  533. add a comment
  534.  
  535. 14
  536.  
  537. A slight revision to the solution e-satis and 3bcdnlklvc04a pointed out in their answer
  538.  
  539. SCRIPT_DIR=''
  540. pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
  541.    SCRIPT_DIR="$PWD"
  542.    popd > /dev/null
  543. }    
  544. This should still work in all the cases they listed.
  545.  
  546. EDIT: prevent popd after failed pushd, thanks to konsolebox
  547.  
  548. shareimprove this answer
  549. edited May 23 '17 at 10:31
  550. community wiki
  551. 3 revs
  552. Fuwjax
  553. This works perfectly to get the "real" dirname, rather than just the name of a symlink. Thank you! – Jay Taylor Jun 23 '10 at 20:32
  554. 1
  555. Better SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; } – konsolebox Jul 3 '14 at 4:15
  556. @konsolebox, what are you trying to defend against? I'm generally a fan of inlining logical conditionals, but what was the specific error that you were seeing in the pushd? I'd match rather find a way to handle it directly instead of returning an empty SCRIPT_DIR. – Fuwjax Jan 19 '15 at 20:03
  557. @Fuwjax Natural practice to avoid doing popd in cases (even when rare) where pushd fails. And in case pushd fails, what do you think should be the value of SCRIPT_DIR? The action may vary depending on what may seem logical or what one user could prefer but certainly, doing popd is wrong. – konsolebox Jan 20 '15 at 19:21
  558. add a comment
  559.  
  560. 13
  561.  
  562. #!/bin/sh
  563. PRG="$0"
  564.  
  565. # need this for relative symlinks
  566. while [ -h "$PRG" ] ; do
  567.    PRG=`readlink "$PRG"`
  568. done
  569.  
  570. scriptdir=`dirname "$PRG"`
  571. shareimprove this answer
  572. answered Sep 13 '08 at 1:28
  573. community wiki
  574. Monkeyboy
  575. add a comment
  576.  
  577. 11
  578.  
  579. $_ is worth mentioning as an alternative to $0. If you're running a script from bash, the accepted answer can be shortened to:
  580.  
  581. DIR="$( dirname "$_" )"
  582. Note that this has to be the first statement in your script.
  583.  
  584. shareimprove this answer
  585. answered Sep 16 '11 at 19:05
  586. community wiki
  587. hurrymaplelad
  588. 3
  589. It breaks if you source or . the script. In those situations, $_ would contain the last parameter of the last command you ran before the .. $BASH_SOURCE works every time. – clacke Jan 31 '14 at 14:55
  590. add a comment
  591.  
  592. 11
  593.  
  594. I've compared many of the answers given, and come up with some more compact solutions. These seem to handle all of the crazy edge cases that arise from your favorite combination of:
  595.  
  596. Absolute paths or relative paths
  597. File and directory soft links
  598. Invocation as script, bash script, bash -c script, source script, or . script
  599. Spaces, tabs, newlines, unicode, etc. in directories and/or filename
  600. Filenames beginning with a hyphen
  601. If you're running from Linux, it seems that using the proc handle is the best solution to locate the fully resolved source of the currently running script (in an interactive session, the link points to the respective /dev/pts/X):
  602.  
  603. resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"
  604. This has a small bit of ugliness to it, but the fix is compact and easy to understand. We aren't using bash primitives only, but I'm okay with that because readlink simplifies the task considerably. The echo X adds an X to the end of the variable string so that any trailing whitespace in the filename doesn't get eaten, and the parameter substitution ${VAR%X} at the end of the line gets rid of the X. Because readlink adds a newline of its own (which would normally be eaten in the command substitution if not for our previous trickery), we have to get rid of that, too. This is most easily accomplished using the $'' quoting scheme, which lets us use escape sequences such as \n to represent newlines (this is also how you can easily make deviously named directories and files).
  605.  
  606. The above should cover your needs for locating the currently running script on Linux, but if you don't have the proc filesystem at your disposal, or if you're trying to locate the fully resolved path of some other file, then maybe you'll find the below code helpful. It's only a slight modification from the above one-liner. If you're playing around with strange directory/filenames, checking the output with both ls and readlink is informative, as ls will output "simplified" paths, substituting ? for things like newlines.
  607.  
  608. absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
  609. dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
  610. file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}
  611.  
  612. ls -l -- "$dir/$file"
  613. printf '$absolute_path: "%s"\n' "$absolute_path"
  614. shareimprove this answer
  615. edited Jul 25 '15 at 19:14
  616. community wiki
  617. 3 revs, 2 users 85%
  618. billyjmc
  619. I get /dev/pts/30 with bash on Ubuntu 14.10 Desktop. – Dan Dascalescu Aug 15 '15 at 11:29
  620. @DanDascalescu Using the one-liner? Or the full code snippet at the bottom? And were you feeding it any tricky pathnames? – billyjmc Aug 19 '15 at 6:34
  621. The one line plus another line to echo $resolved, I saved it as d, chmod +x d, ./d. – Dan Dascalescu Aug 20 '15 at 5:55
  622. @DanDascalescu The first line in your script needs to be #!/bin/bash – billyjmc Aug 23 '15 at 5:07
  623. @DanDascalescu See stackoverflow.com/a/12296783/558709 – billyjmc Aug 23 '15 at 5:17
  624. add a comment
  625.  
  626. 10
  627.  
  628. For systems having GNU coreutils readlink (eg. linux):
  629.  
  630. $(readlink -f "$(dirname "$0")")
  631. No need to use BASH_SOURCE when $0 contains the script filename.
  632.  
  633. shareimprove this answer
  634. edited May 13 '18 at 4:34
  635. community wiki
  636. 2 revs
  637. user1338062
  638. 1
  639. unless the script was sourced with . or 'source' in which case it will still be whatever script sourced it, or, if from the command line, '-bash' (tty login) or 'bash' (invoked via 'bash -l') or '/bin/bash' (invoked as an interactive non-login shell) – osirisgothra Feb 5 '15 at 0:07
  640. I added second pair of quotes around dirname call. Needed if the directory path contains spaces. – user1338062 May 13 '18 at 4:35
  641. add a comment
  642.  
  643. 9
  644.  
  645. Try using:
  646.  
  647. real=$(realpath $(dirname $0))
  648. shareimprove this answer
  649. edited Feb 6 '12 at 11:20
  650. community wiki
  651. 2 revs, 2 users 60%
  652. eeerahul
  653. All I want to know is, why this way is not good? It seemed no bad and correct for me. Could anyone explain why it's downvoted? – Shou Ya Aug 28 '12 at 15:16
  654. 6
  655. realpath is not a standard utility. – Steve Bennett May 13 '13 at 12:06
  656. On Linux, realpath is a standard utility (part of the GNU coreutils package), but it is not a bash built-in (i.e., a function provided by bash itself). If you're running Linux, this method will probably work, although I'd substitute the $0 for ${BASH_SOURCE[0]} so that this method will work anywhere, including in a function. – Doug Richardson Jul 18 '14 at 15:53
  657. 2
  658. The order of the operations in this answer is wrong. You need to first resolve the symlink, then do dirname because the last part of $0 may be a symlink that points to a file that is not in the same directory as the symlink itself. The solution described in this answer just gets the path of the directory where the symlink it stored, not the directory of the target. Furthermore, this solution is missing quoting. It will not work if the path contains special characters. – hagello Apr 13 '15 at 21:43
  659. This solution "just gets the path of the directory where the symlink it stored", and that's what I actually want. PS: quoting still should be added. – Kostiantyn Ponomarenko Mar 11 at 10:19
  660. show 1 more comment
  661.  
  662. 8
  663.  
  664. So... I believe I've got this one. Late to the party, but I think some will appreciate it being here is them come across this thread. The comments should explain.
  665.  
  666. #!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.
  667.  
  668. ## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
  669. ## dereference symbolic links (ala 'readlink') until the originating file
  670. ## is found. This is effectively the same function provided in stdlib.h as
  671. ## 'realpath' and on the command line in GNU 'readlink -f'.
  672.  
  673. ## Neither of these tools, however, are particularly accessible on the many
  674. ## systems that do not have the GNU implementation of readlink, nor ship
  675. ## with a system compiler (not to mention the requisite knowledge of C).
  676.  
  677. ## This script is written with portability and (to the extent possible, speed)
  678. ## in mind, hence the use of printf for echo and case statements where they
  679. ## can be substituded for test, though I've had to scale back a bit on that.
  680.  
  681. ## It is (to the best of my knowledge) written in standard POSIX shell, and
  682. ## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
  683. ## issues with it, though I'm not sure why; so probably best to avoid for now.
  684.  
  685. ## Particularly useful (in fact, the reason I wrote this) is the fact that
  686. ## it can be used within a shell script to find the path of the script itself.
  687. ## (I am sure the shell knows this already; but most likely for the sake of
  688. ## security it is not made readily available. The implementation of "$0"
  689. ## specificies that the $0 must be the location of **last** symbolic link in
  690. ## a chain, or wherever it resides in the path.) This can be used for some
  691. ## ...interesting things, like self-duplicating and self-modifiying scripts.
  692.  
  693. ## Currently supported are three errors: whether the file specified exists
  694. ## (ala ENOENT), whether its target exists/is accessible; and the special
  695. ## case of when a sybolic link references itself "foo -> foo": a common error
  696. ## for beginners, since 'ln' does not produce an error if the order of link
  697. ## and target are reversed on the command line. (See POSIX signal ELOOP.)
  698.  
  699. ## It would probably be rather simple to write to use this as a basis for
  700. ## a pure shell implementation of the 'symlinks' util included with Linux.
  701.  
  702. ## As an aside, the amount of code below **completely** belies the amount
  703. ## effort it took to get this right -- but I guess that's coding for you.
  704.  
  705. ##===-------------------------------------------------------------------===##
  706.  
  707. for argv; do :; done # Last parameter on command line, for options parsing.
  708.  
  709. ## Error messages. Use functions so that we can sub in when the error occurs.
  710.  
  711. recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
  712. dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
  713. errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.
  714.  
  715. # Probably best not to install as 'pathfull', if you can avoid it.
  716.  
  717. pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"
  718.  
  719. ## 'test and 'ls' report different status for bad symlinks, so we use this.
  720.  
  721. if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
  722.    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
  723.    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
  724.    dangling 1>&2; exit 1; fi
  725. fi
  726.  
  727. ## Not a link, but there might be one in the path, so 'cd' and 'pwd'.
  728.  
  729. if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
  730.   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
  731. fi
  732.  
  733. ## Walk the symlinks back to the origin. Calls itself recursivly as needed.
  734.  
  735. while [ "$link" ]; do
  736.   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
  737.   case "$newlink" in
  738.    "$link") dangling 1>&2 && exit 1                                       ;;
  739.         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
  740.          *) link="$newlink" && pathfull "$link"                           ;;
  741.   esac
  742. done
  743. printf "$(pwd)/$(basename "$newlink")\n"
  744. }
  745.  
  746. ## Demo. Install somewhere deep in the filesystem, then symlink somewhere
  747. ## else, symlink again (maybe with a different name) elsewhere, and link
  748. ## back into the directory you started in (or something.) The absolute path
  749. ## of the script will always be reported in the usage, along with "$0".
  750.  
  751. if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"
  752.  
  753. # Yay ANSI l33t codes! Fancy.
  754. printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
  755. printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
  756. printf "Recursive readlink for the authoritative file, symlink after "
  757. printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
  758. printf " From within an invocation of a script, locate the script's "
  759. printf "own file\n         (no matter where it has been linked or "
  760. printf "from where it is being called).\n\n"
  761.  
  762. else pathfull "$@"
  763. fi
  764. shareimprove this answer
  765. answered Dec 21 '13 at 17:47
  766. community wiki
  767. Geoff Nixon
  768. add a comment
  769.  
  770. 8
  771.  
  772. Try the following cross-compatible solution:
  773.  
  774. CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
  775. as realpath or readlink commands are not always available (depending on the operating system) and ${BASH_SOURCE[0]} is available only in bash shell.
  776.  
  777. Alternatively you can try the following function in bash:
  778.  
  779. realpath () {
  780.  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
  781. }
  782. This function takes 1 argument. If argument has already absolute path, print it as it is, otherwise print $PWD variable + filename argument (without ./ prefix).
  783.  
  784. Related:
  785.  
  786. How to set current working directory to the directory of the script?
  787. Bash script absolute path with OSX
  788. Reliable way for a bash script to get the full path to itself?
  789. shareimprove this answer
  790. edited Apr 24 '15 at 2:23
  791. community wiki
  792. 4 revs
  793. kenorb
  794. Please explain more about the realpath function. – Chris Mar 27 '15 at 16:54
  795. 1
  796. @Chris realpath function takes 1 argument. If argument has already absolute path, print it as it is, otherwise print $PWD + filename (without ./ prefix). – kenorb Mar 27 '15 at 17:35
  797. Your cross-compatible solution doesn’t work when the script is symlinked. – Jakub Jirutka Sep 8 '15 at 20:59
  798. add a comment
  799.  
  800. 7
  801.  
  802. Hmm, if in the path basename & dirname are just not going to cut it and walking the path is hard (what if parent didn't export PATH!). However, the shell has to have an open handle to its script, and in bash the handle is #255.
  803.  
  804. SELF=`readlink /proc/$$/fd/255`
  805. works for me.
  806.  
  807. shareimprove this answer
  808. edited Jul 27 '11 at 20:17
  809. community wiki
  810. 2 revs, 2 users 86%
  811. Joshua
  812. I get /dev/pts/30 with bash on Ubuntu 14.10 Desktop, instead of the actual directory I run the script from. – Dan Dascalescu Aug 15 '15 at 11:30
  813. add a comment
  814.  
  815. 6
  816.  
  817. This works in bash-3.2:
  818.  
  819. path="$( dirname "$( which "$0" )" )"
  820. Here's an example of its usage:
  821.  
  822. Say you have a ~/bin directory, which is in your $PATH. You have script A inside this directory. It sources script ~/bin/lib/B. You know where the included script is relative to the original one (the subdirectory lib), but not where it is relative to the user's current directory.
  823.  
  824. This is solved by the following (inside A):
  825.  
  826. source "$( dirname "$( which "$0" )" )/lib/B"
  827. It doesn't matter where the user is or how he calls the script, this will always work.
  828.  
  829. shareimprove this answer
  830. edited Sep 3 '10 at 6:44
  831. community wiki
  832. 5 revs, 2 users 97%
  833. Matt Tardiff
  834. 2
  835. The point on which is very debatable. type, hash, and other builtins do the same thing better in bash. which is kindof more portable, though it really isn't the same which used in other shells like tcsh, that has it as a builtin. – BroSlow Jan 13 '14 at 22:30
  836. "Always"? Not at all. which being an external tool, you have no reason to believe it behaves identically to the parent shell. – Charles Duffy Jun 9 '14 at 3:42
  837. add a comment
  838.  
  839. 5
  840.  
  841. None of these worked for a bash script launched by Finder in OS X - I ended up using:
  842.  
  843. SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
  844. sed s:.*/Volumes/:/Volumes/:`"
  845. Not pretty, but it gets the job done.
  846.  
  847. shareimprove this answer
  848. edited Feb 14 '13 at 17:23
  849. community wiki
  850. 2 revs, 2 users 67%
  851. tigfox
  852. add a comment
  853.  
  854. 5
  855.  
  856. The best compact solution in my view would be:
  857.  
  858. "$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"
  859. There is no reliance on anything other than Bash. The use of dirname, readlink and basename will eventually lead to compatibility issues, so they are best avoided if at all possible.
  860.  
  861. shareimprove this answer
  862. answered Oct 8 '13 at 14:24
  863. community wiki
  864. AsymLabs
  865. 1
  866. You probably should add slash to that: "$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )". You'd have problems with root directory if you don't. Also why do you even have to use echo? – konsolebox Jul 2 '14 at 17:21
  867. dirname and basename are POSIX standardized, so why avoid using them? Links: dirname, basename – myrdd Oct 29 '18 at 9:18
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement