4

I would like my bash script to check the name of the directory where it is run. Something like:

#!/bin/bash

path=eval 'pwd'

dirname=eval 'basename $path'

But it doesn't work: I get

./foo.sh: line 5: basename $path: command not found

How can I fix it? Also, once I get dirname to contain the correct dirname, I'd like to convert it to lowercase, to test it. I'm able to do this on the command line with awk:

echo $dirname | awk '{print tolower($0)}'

but how do I capture the return value into a variable?

Nimantha
  • 6,405
  • 6
  • 28
  • 69
DeltaIV
  • 4,773
  • 12
  • 39
  • 86

7 Answers7

3

Why not use:

#!/bin/bash

path=`pwd`
dirname=`basename $path | awk '{print tolower($0)}'`

Or if you want to do it as a one liner:

dirname=`pwd | xargs basename | awk '{print tolower($0)}'`
Benj
  • 31,668
  • 17
  • 78
  • 127
  • Thanks! Works great. Unfortunately, my keyboard doesn't have right ticks :( luckily, I was able to copy and paste your code in my script. – DeltaIV Oct 02 '12 at 13:16
  • @DeltaIV Good to hear! I suppose you could always type ALT-96 to get it :-) – Benj Oct 02 '12 at 13:21
  • 1
    Use `$(pwd)` instead of backticks. They nest more easily, and are more visible than backticks. Plus, you have those keys on your keyboard :) – chepner Oct 02 '12 at 15:57
2

You can rewrite it to

dirname=eval "basename $path"

With single-quotes, you don't get shell expansion, but you want $path getting expanded.

BTW: I'd suggesst using

path=$(basename $path)

It's way more generic and better readable if you do something like

path=$(basename $(pwd))

or to get the lowercase result

path=$(basename $(pwd) | awk '{print tolower($0)}')

or

path=$(basename $(pwd) | tr 'A-Z' 'a-z' )
Mark
  • 6,033
  • 1
  • 19
  • 14
2

The form

x=y cmd

means to temporarily set environment variable x to value y and then run cmd, which is how these lines are interpreted:

path=eval 'pwd'
dirname=eval 'basename $path'

That is, they aren't doing what you seem to expect at all, instead setting an environment variable to the literal value eval and then running (or failing to find) a command. As others have said, the way to interpolate the results of a command into a string is to put it inside $(...) (preferred) or `...` (legacy). And, as a general rule, it's safer to wrap those in double quotes (as it is safer to wrap any interpolated reference in quotes).

path="$(pwd)"
dirname="$(basename "$path")"

(Technically, in this case the outer quotes aren't strictly necessary. However, I'd say it's still a good habit to have.)

danfuzz
  • 4,253
  • 24
  • 34
0
B=$(echo "Some text that has CAPITAL letters " | awk '{print tolower($0)}')
Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
0

eval executes command passed to it, but it returns only command exit status code, so you cannot really use it in set operator. The way to go to embed command into set operator either to use right single quotes or $()

So the script will look like this:

#!/bin/bash

curr_path=$(pwd)
echo $curr_path
curr_dir=$(basename $curr_path)
echo $curr_dir
echo $curr_dir | awk '{print tolower($0)}'
divanov
  • 6,173
  • 3
  • 32
  • 51
0

Your code doesn't work because you use single quotes rather than double quotes. Single quotes prevent variable expansion, thus $path is not expanded into the path you want to use and is taken as it is, as it if were a string.

Your awk invocation would not work for the same reason as well.

Although you could solve the problem replacing single quotes with double quotes, like this:

#!/bin/bash

path=eval "pwd"
dirname=eval "basename $path"

I would suggest using grave accents instead (). There's no reason to useeval` in this case. Plus, you can also use it to collect the return value you are interested in:

#!/bin/bash

path=`pwd`
dirname=`basename $path`
variable=`echo $dirname | awk "{print tolower($0)}"`
pb2q
  • 58,613
  • 19
  • 146
  • 147
Ferdinando Randisi
  • 4,068
  • 6
  • 32
  • 43
0

Here's an excerpt from my answer to What platform independent way to find directory of shell executable in shell script? which, in itself, fully answers your question aside from the lowercase part, which, in my opinion, has been duly addressed many times in other answers here.

What's unique about my answer is that when I was attempting to write it for the other question I encountered your exact problem - how do I store the function's results in a variable? Well, as you can see, with some help, I hit upon a pretty simple and very powerful solution:

I can pass the function a sort of messenger variable and dereference any explicit use of the resulting function's argument's $1 name with eval as necessary, and, upon the function routine's completion, I use eval and a backslashed quoting trick to assign my messenger variable the value I desire without ever having to know its name.

In full disclosure, though this was the solution to my problem, it was not by any means my solution. I've had several occasions to visit there before, but some of his descriptions, though probably brilliant, are a little out of my league, and so I thought others might benefit if include my own version of how this works in the previous paragraph. Though of course it was very simple to understand once I did, for this one especially, I had to think long and hard to figure out how it might work. Anyway, you can find that and more at Rich's sh tricks and I have also excerpted the relevant portion of his page below my own answer's excerpt.

... EXCERPT: ...

Though not strictly POSIX yet, realpath is a GNU core app since 2012. Full disclosure: never heard of it before I noticed it in the info coreutils TOC and immediately thought of [the linked] question, but using the following function as demonstrated should reliably, (soon POSIXLY?), and, I hope, efficiently provide its caller with an absolutely sourced $0:

% _abs_0() { 
> o1="${1%%/*}"; ${o1:="${1}"}; ${o1:=`realpath "${1}"`}; eval "$1=\${o1}"; 
> }  
% _abs_0 ${abs0:="${0}"} ; printf %s\\n "${abs0}"
/no/more/dots/in/your/path2.sh

EDIT: It may be worth highlighting that this solution uses POSIX parameter expansion to first check if the path actually needs expanding and resolving at all before attempting to do so. This should return an absolutely sourced $0via a messenger variable (with the notable exception that it will preserve symlinks) as efficiently as I could imagine it could be done whether or not the path is already absolute. ...

(minor edit: before finding realpath in the docs, I had at least pared down my version of [the version below] not to depend on the time field [as it does in the first ps command], but, fair warning, after testing some I'm less convinced ps is fully reliable in its command path expansion capacity)

On the other hand, you could do this:

ps ww -fp $$ | grep -Eo '/[^:]*'"${0#*/}"

eval "abs0=${`ps ww -fp $$ | grep -Eo ' /'`#?}"

... And from Rich's sh tricks: ...

Returning strings from a shell function

As can be seen from the above pitfall of command substitution, stdout is not a good avenue for shell functions to return strings to their caller, unless the output is in a format where trailing newlines are insignificant. Certainly such practice is not acceptable for functions meant to deal with arbitrary strings. So, what can be done?

Try this:

func () {
body here
eval "$1=\${foo}"
}

Of course ${foo} could be replaced by any sort of substitution. The key trick here is the eval line and the use of escaping. The “$1” is expanded when the argument to eval is constructed by the main command parser. But the “${foo}” is not expanded at this stage, because the “$” has been quoted. Instead, it’s expanded when eval evaluates its argument. If it’s not clear why this is important, consider how the following would be bad:

foo='hello ; rm -rf /'
dest=bar
eval "$dest=$foo"

But of course the following version is perfectly safe:

foo='hello ; rm -rf /'
dest=bar
eval "$dest=\$foo"

Note that in the original example, “$1” was used to allow the caller to pass the destination variable name as an argument the function. If your function needs to use the shift command, for instance to handle the remaining arguments as “$@”, then it may be useful to save the value of “$1” in a temporary variable at the beginning of the function.

Community
  • 1
  • 1
mikeserv
  • 694
  • 7
  • 9