0

These lines work when copy-pasted to the shell but don't work in a script:

ls -l file1 > /path/`echo !#:2`.txt
ls -l file2 > /path/`echo !#:2`.txt

 

ls -l file1 > /path/$(echo !#:2).txt
ls -l file2 > /path/$(echo !#:2).txt

What's the syntax for doing this in a bash script?

If possible, I would like to know how to do this for one file and for all files with the same extension in a folder.

0

2 Answers 2

3

Non-interactive shell has history expansion disabled.

Add the following two lines to your script to enable it:

set -o history
set -o histexpand
Sign up to request clarification or add additional context in comments.

Comments

1

(UPDATE: I misunderstood the original question as referring to arguments to the script, not arguments to the current command within the script; this is a rewritten answer.)

As @choroba said, history is disabled by default in scripts, because it's not really the right way to do things like this in a script.

The preferred way to do things like this in a script is to store the item in question (in this case the filename) in a variable, then refer to it multiple times in the command:

fname=file1
ls -l "$fname" > "/path/$fname.txt"

Note that you should almost always put variable references inside double-quotes (as I did above) to avoid trouble if they contain spaces or other shell metacharacters. If you want to do this for multiple files, use a for loop:

for fname in *; do   # this will repeat for each file (or directory) in the current directory
    ls -l "$fname" > "/path/$fname.txt"
done

If you want to operate on files someplace other than the current directory, things are a little more complicated. You can use /inputpath/*, but it'll include the path along with each filename (e.g. it'd run the loop with "/inputpath/file1", "/inputpath/file2", etc), and if you use that directly in the output redirect you'll get something like > /path/inputpath/file1.txt (i.e. the two different paths will get appended together), probably not what you want. In this case, you can use the basename command to strip off the unwanted path for output purposes:

for fpath in /inputpath/*; do
    ls -l "$fpath" > "/path/$(basename "$fpath").txt"
done

If you want a list of files with a particular extension, just use *.foo or /inputpath/*.foo as appropriate. However, in this case you'll wind up with the output going to files named e.g. "file1.foo.txt"; if you don't want stacked extensions, basename has an option to trim that as well:

for fpath in /inputpath/*.foo; do
    ls -l "$fpath" > "/path/$(basename "$fpath" .foo).txt"
done

Finally, it might be neater (depending how complex the actual operation is, and whether it occurs multiple times in the script) to wrap this in a function, then use that:

doStuffWithFile() {
    ls -l "$1" > "/path/$(basename "$1" "$2").txt"
}

for fpath in /inputpath/*.foo; do
    doStuffWithFile "$fpath" ".foo"
done
doStuffWithFile /otherpath/otherfile.bar .bar

1 Comment

Um, there's probably a misunderstanding or I have a completely botched version of Bash. When I do what you've said I have the output saved to /path/.txt. What I want is an individual output file for every line named after the command line argument, so if the argument is foo I want an output file named foo.txt. If the second argument is file2 I want another output file named file2.txt. Obviously, it wouldn't make much sense doing this with ls -l, I only chose it to demonstrate my request because explaining what I actually need this for would be a bit complicated.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.