0

I am trying to make a bash script that takes a file as input. The script works fine if I have the file declared in the script normally, but when I try to make it a dynamic variable, by dropping the file into the script it breaks.

e.g.

#!/bin/bash

input="$HOME/Desktop/test.txt"

if [ -f "$input" ]; then
    echo "is file"
else
    echo "is NOT file"
fi

returns:

is file

Where as

#!/bin/bash

read input

if [ -f "$input" ]; then
    echo "is file"
else
    echo "is NOT file"
fi

With the file "$HOME/Desktop/test.txt" dropped into the terminal returns:

is NOT file

I'm not clear as to why dropping the file changes it from not a file. I have tried many variation to no avail.

nothing
  • 77
  • 1
  • 5

1 Answers1

1

When you drag and drop a fille in your graphical environment, the shell probably includes additional characters (such as quotes) in the string it provides. Your script tries to find a file that is exactly what the shell provides (i.e. quotes included) and does not find one.

Initial solution - Not recommended, but left here because it worked for the original poster

You could try this :

read input
printf -v input "$(eval echo "$input")"

That printf statement does two things : it uses the -v input to assign a new value to the input variable. The value it assigns is the output of eval echo "$input", captured with a command substitution $(). eval forces expansion to be performed on the arguments it is passed before executing them as a command. The command ends up being echo with its value being the (expanded) initial value of variable input.

Remember : read does not perform expansion or word splitting, if just takes what you type and puts in in a variable, as is.

Please note that use of eval opens up code injection risks. A specially crafted value could cause arbitrary code execution by eval, and the code I am proposing does not protect from that. There are safer, better ways to do that if the exact format of the string sent to the terminal window is known.

Update - Recommendation based on additional details from original poster

For instance, if you know the file name is inside single quotes, you could do this (which will also ignore leading and trailing whitespace) :

read input
if
  [[ $input =~ ^[[:space:]]*\'(.*)\'[[:space:]]*$ ]]
then
  input="${BASH_REMATCH[1]}"
fi

This code snippet will extract the file name from inside the single quotes if they are there, or will leave the input as is otherwise, and is safer from a code injection standpoint.

Fred
  • 6,590
  • 9
  • 20
  • I may have miscommunicated what was actually happening when I dropped the file into the terminal. When I did the drag and drop the full path of "/home/user/Desktop/test.txt" would appear in the terminal and still return is NOT file. That said your solution does work for me, but does what I've said change anything or do I need to use eval either way? – nothing Feb 06 '17 at 04:25
  • @nothing I suspect (but cannot verify) that your drag and drop operation adds something at the beginning/end of your string (e.g. carriage return, space, whatever), and that this causes your issue. Try adding the following command to your script (just after the `read`) to see what output it creates : `echo "-${input}-"`. If you see anything weird or extra spacing/newlines between the dashes and the file name, then this is your problem. – Fred Feb 06 '17 at 04:30
  • Upon trying your advice it looks like the issue might be when I drop the file in it comes out as -'/home/user/Desktop/test.txt'- I thought the ' was keeping the string complete but it seems like the shell might be interpreting the as a character in the file name. – nothing Feb 06 '17 at 04:37
  • @nothing I think that behaviour depends on your terminal emulator. For mine (Gnome terminal), dropping a file inserts the full path, in single quotes, with a space at the end. If you store that in a variable, the shell tries to find a file with the name `'/home/user/Desktop.txt'` (with literal quotes) and a space at the end, which doesn't exist. – Benjamin W. Feb 06 '17 at 04:43
  • That is the same input that I get (using MATE terminal.) But again when running the script Fred recommended it seems to drop the trailing space and only return what is between the literals, although including the literals as well. My thought process is the terminal would not interpret the literal as a separate character by the "if" command, but that is all I can figure at this point. – nothing Feb 06 '17 at 04:52