The "read" command misinterprets file names containing spaces


 
Thread Tools Search this Thread
Top Forums Shell Programming and Scripting The "read" command misinterprets file names containing spaces
# 8  
Old 01-09-2012
Alternative approach. Ask the user to enter the filenames one by one.
This will work for filenames with or without space characters.
In this script, entering a blank filename means end-of-list and we "break" out of the "while true" loop.
Notice that we always have double quotes round a filename if it might contain space characters.

Code:
while true
do
        read -p "Enter filename: " filename
        if [ "${filename}""X" = "X" ]
        then
                break
        fi
        if [ -f "${filename}" ]
        then
               ls -lad "${filename}"
        else
               echo "File does not exist: ${filename}"
        fi
done

# 9  
Old 01-10-2012
Quote:
Originally Posted by Chubler_XL
Use bash to read the into an array:
Code:
read -p "Enter files: " -a vals
 
for((i=0; i < ${#vals[@]}; i++)) do
  echo "$i: ${vals[i]}"
done



Thank you, Chubler_XL. It is a great progress that backslashes now work as I wanted. However, I still need more way to go. I would like the wildcard asterisks (*) to work. I also want quotes to work.

In other words, I would like my script to take file names (or pathnames) from the user like arguments to the "rm" command, except that my script does not take options. I would like my script to interpret backslashes, quotes, wildcard asterisks (*) and delimiting spaces in the same manner as arguments to the "rm" command.

Last edited by LessNux; 01-10-2012 at 03:01 PM..
# 10  
Old 01-10-2012
The only real option I can think of here (short of implementing all these expansions yourself) is the use of the eval command.

Problem is this opens you up to command injection. For example the filename could be $(chmod 777 /home/lessnux/.profile)

Code:
#!/bin/bash
process_files()
{
 while [ $# -gt 0 ]
 do
    echo "filename: " $1
    shift
 done
}
 
read -rp "Enter files: " files
eval process_files $files


Code:
Enter files: "Summer View.txt" Winter\ View.txt /etc/pro*
filename:  Summer View.txt
filename:  Winter View.txt
filename:  /etc/profile
filename:  /etc/protocols

# 11  
Old 01-13-2012
Quote:
Originally Posted by LessNux
I would like my script to take file names (or pathnames) from the user like arguments to the "rm" command, except that my script does not take options. I would like my script to interpret backslashes, quotes, wildcard asterisks (*) and delimiting spaces in the same manner as arguments to the "rm" command.

Quote:
Originally Posted by Chubler_XL
the use of the eval command
Code:
#!/bin/bash
process_files()
{
 while [ $# -gt 0 ]
 do
    echo "filename: " $1
    shift
 done
}

read -rp "Enter files: " files
eval process_files $files



Thank you, Chubler_XL, for a further improvement. However, the code does not handle wildcard asterisks (*) very well, if the file intended to be matched contains a space.

Suppose that the current directory has the following four files. Also suppose that the name of the above code supplied by Chubler_XL is "readtest.sh" and located at the parent directory of the current one.

Summer View.txt
Winter View.txt
scene 1.sh
scene 2.sh


Because it is dangerous to experiment with "rm", let us use "ls" instead. I would like to allow the user to input file names in the same manner as arguments to the "ls" command, except that my script does not take options.

First, I gave the following line to "ls" and "readtest.sh".

"Summer View.txt" Winter\ View.txt scene*.sh

The results were a success with "ls" and a failure with "readtest.sh". The "readtest.sh" script interpreted "scene" and "1.sh" as two separate files.

Code:
$ ls -1 "Summer View.txt" Winter\ View.txt scene*.sh
Summer View.txt
Winter View.txt
scene 1.sh
scene 2.sh
$ 
$ 
$ ../readtest.sh
Enter files: "Summer View.txt" Winter\ View.txt scene*.sh
filename:  Summer View.txt
filename:  Winter View.txt
filename:  scene
filename:  1.sh
filename:  scene
filename:  2.sh


Second, I gave the following line to "ls" and "readtest.sh".

"Summer View.txt" Winter\ View.txt "scene*.sh"

Both "ls" and "readtest.sh" failed. The "readtest.sh" script mistook as if "scene 1.sh scene 2.sh" were a single file with a long name.

Code:
$ ls -1 "Summer View.txt" Winter\ View.txt "scene*.sh"
ls: scene*.sh: No such file or directory
Summer View.txt
Winter View.txt
$ 
$ 
$ ../readtest.sh
Enter files: "Summer View.txt" Winter\ View.txt "scene*.sh"
filename:  Summer View.txt
filename:  Winter View.txt
filename:  scene 1.sh scene 2.sh


If "ls" correctly understands scene*.sh without quotes for "scene 1.sh" and "scene 2.sh", then I would like the script to understand it in the same manner as "ls".

If "ls" does not understand "scene*.sh" with quotes, then it will not matter to me whether the script understands it or not.

Many thanks, in advance.
# 12  
Old 01-13-2012
Quote:
Originally Posted by LessNux
If "ls" correctly understands scene*.sh without quotes for "scene 1.sh" and "scene 2.sh", then I would like the script to understand it in the same manner as "ls".
It's got nothing to do with ls. ls doesn't know what * means.

The shell script understands that, if you're doing globbing, you want literal unmangled filenames, so * gets you literal unmangled filenames. The shell also understands that "thing in spaces" means a literal string and doesn't split it.

If your actual string contains quotes, that's an entirely different thing than a quoted string, though. To get that string in shell you'd have to do "\"thing in quotes\"". The shell doesn't unwrap the second layer of quotes because it thinks you want them, and won't do that kind of double think unless you tell it to -- which is a good thing, because if it did process everything it found in a variable, someone could type `sudo rm -Rf /` into your input and wipe your system.

That sort of shenanigans is why I wouldn't reccomend using eval to force the shell to evaluate the quotes, either. It'd technically work but would be a frightening security hole.

xargs also processes quotes, though! I've spent more time fighting that feature than using it, forcing xargs to use raw filenames containing spaces or quotes, but it might actually be useful here...

Code:
# Green is typed input, red is what xargs prints
$ xargs printf "%s\n"
"file with spaces" file_without_spaces
^D
file with spaces
file_without_spaces
$

So:

Code:
printf "Enter list of files: "
read LINE

echo "$LINE" | xargs printf "%s\n" > /tmp/$$
while read FILENAME
do
        echo "Got filename $FILENAME"
done < /tmp/$$
rm -f /tmp/$$


Last edited by Corona688; 01-13-2012 at 01:57 PM.. Reason: typos
# 13  
Old 01-15-2012
As you can tell LessNux we are very concerned with the security implications of using eval against user entered data. There are a few limited situations where using eval could be OK but in general it's pretty unsafe.

If you really need to do wildcard filename expansion/matching on userdata perhaps something in perl eg:
Code:
#!/usr/bin/perl
use strict;
use warnings;
my @files = glob("Winter\\ View.txt \"Summer View.txt\" scene*");
foreach my $file (@files) {
        print "Filename: $file\n";
}
exit 0;

This User Gave Thanks to Chubler_XL For This Post:
# 14  
Old 01-18-2012
Quote:
Originally Posted by Chubler_XL
Code:
#!/usr/bin/perl
use strict;
use warnings;
my @files = glob("Winter\\ View.txt \"Summer View.txt\" scene*");




My BASH script was supposed to call a command that takes file names (or pathnames). Suppose that the name of the command is TweakFile, and that its syntax is
TweakFile FILE...

After the perl function glob() parsed and expanded file names, how can I pass it to TweakFile on BASH?

Many thanks, in advance.

---------- Post updated at 10:27 AM ---------- Previous update was at 10:19 AM ----------

Quote:
Originally Posted by Corona688
xargs also processes quotes


Does xargs parse and expand file names without execution?

Many thanks, in advance.
Login or Register to Ask a Question

Previous Thread | Next Thread

10 More Discussions You Might Find Interesting

1. Shell Programming and Scripting

Bash script - Print an ascii file using specific font "Latin Modern Mono 12" "regular" "9"

Hello. System : opensuse leap 42.3 I have a bash script that build a text file. I would like the last command doing : print_cmd -o page-left=43 -o page-right=22 -o page-top=28 -o page-bottom=43 -o font=LatinModernMono12:regular:9 some_file.txt where : print_cmd ::= some printing... (1 Reply)
Discussion started by: jcdole
1 Replies

2. UNIX for Dummies Questions & Answers

Using "mailx" command to read "to" and "cc" email addreses from input file

How to use "mailx" command to do e-mail reading the input file containing email address, where column 1 has name and column 2 containing “To” e-mail address and column 3 contains “cc” e-mail address to include with same email. Sample input file, email.txt Below is an sample code where... (2 Replies)
Discussion started by: asjaiswal
2 Replies

3. UNIX for Dummies Questions & Answers

Unix "look" Command "File too large" Error Message

I am trying to find lines in a text file larger than 3 Gb that start with a given string. My command looks like this: $ look "string" "/home/patrick/filename.txt" However, this gives me the following message: "look: /home/patrick/filename.txt: File too large" So, I have two... (14 Replies)
Discussion started by: shishong
14 Replies

4. Shell Programming and Scripting

awk command to replace ";" with "|" and ""|" at diferent places in line of file

Hi, I have line in input file as below: 3G_CENTRAL;INDONESIA_(M)_TELKOMSEL;SPECIAL_WORLD_GRP_7_FA_2_TELKOMSEL My expected output for line in the file must be : "1-Radon1-cMOC_deg"|"LDIndex"|"3G_CENTRAL|INDONESIA_(M)_TELKOMSEL"|LAST|"SPECIAL_WORLD_GRP_7_FA_2_TELKOMSEL" Can someone... (7 Replies)
Discussion started by: shis100
7 Replies

5. Shell Programming and Scripting

Compare file names and select correct elements to include in "for each loop"

Hi everyone, I`ll try to be most clear I can explaining my help request. I have 2 folders Folder A-->This folder receives files through FTP constantly Folder B-->The files from Folder A are unzipped and then processed in Folder B Sometimes Folder A doesn`t contain all... (2 Replies)
Discussion started by: cgkmal
2 Replies

6. Shell Programming and Scripting

"read" command ignoring leading spaces

I have to read a file line by line, change it and then update the file. Problem is, when i read the file, "read" command ignores leading spaces. The file is a script which is indented in many places for clarity. How to i make "read" command read leading spaces as well. (3 Replies)
Discussion started by: vickylife
3 Replies

7. Shell Programming and Scripting

read -p "prompt text" foo say "read: bad option(s)" in Bourne-Shell

Hallo, i need a Prompting read in my script: read -p "Enter your command: " command But i always get this Error: -p: is not an identifier When I run these in c-shell i get this error /usr/bin/read: read: bad option(s) How can I use a Prompt in the read command? (9 Replies)
Discussion started by: wiseguy
9 Replies

8. Shell Programming and Scripting

script to read a line with spaces bet " " and write to a file

Hi, I need a command in UNIX KSH below is the description... MAPPING DESCRIPTION ="Test Mapping for the calid inputs" ISVALID ="YES" NAME ="m_test_xml" OBJECTVERSION ="1" VERSIONNUMBER ="1" unix ksh command to read the DESCRIPTION and write to a file Test Mapping for the calid inputs... (3 Replies)
Discussion started by: perlamohan
3 Replies

9. Shell Programming and Scripting

How to remove "New line characters" and "spaces" at a time

Dear friends, following is the output of a script from which I want to remove spaces and new-line characters. Example:- Line1 abcdefghijklmnopqrstuvwxyz Line2 mnopqrstuvwxyzabcdefghijkl Line3 opqrstuvwxyzabcdefdefg Here in above example, at every starting line there is a “tab” &... (4 Replies)
Discussion started by: anushree.a
4 Replies

10. Shell Programming and Scripting

passing a list of dynamic names to a "PS" command in shell script?

Hi, I am new to shell script. This is my first post .I have written a small script which returns list of names starts with "ram" in /etc/passwd .Here is that:- #!/bin/ksh NAME_LIST="name_list.txt" cat /dev/null > $NAME_LIST evalcmd="cat /etc/passwd | grep "^ram?*" | cut -d: -f1" eval... (3 Replies)
Discussion started by: sachin.tendulka
3 Replies
Login or Register to Ask a Question