Using the result of a command as an argument in bash? - bash

To create a playlist for all of the music in a folder, I am using the following command in bash:
ls > list.txt
I would like to use the result of the pwd command for the name of the playlist.
Something like:
ls > ${pwd}.txt
That doesn't work though - can anyone tell me what syntax I need to use to do something like this?
Edit: As mentioned in the comments pwd will end up giving an absolute path, so my playlist will end up being named .txt in some directory - d'oh! So I'll have to trim the path. Thanks for spotting that - I would probably have spent ages wondering where my files went!

The best way to do this is with "$(command substitution)" (thanks, Landon):
ls > "$(pwd).txt"
You will sometimes also see people use the older backtick notation, but this has several drawbacks in terms of nesting and escaping:
ls > "`pwd`.txt"
Note that the unprocessed substitution of pwd is an absolute path, so the above command creates a file with the same name in the same directory as the working directory, but with a .txt extension. Thomas Kammeyer pointed out that the basename command strips the leading directory, so this would create a text file in the current directory with the name of that directory:
ls > "$(basename "$(pwd)").txt"
Also thanks to erichui for bringing up the problem of spaces in the path.

This is equivalent to the backtick solution:
ls > $(pwd).txt

To do literally what you said, you could try:
ls > `pwd`.txt
which will use the full pathname, which should be fine.
Note that if you do this in your home directory, which might
be in /home/hoboben, you will be trying the create /home/hoboben.txt,
a text file in the directory above.
Is this what you wanted?
If you wanted the directory to contain a file named after it, you would get
the basename of the current directory and append that with .txt to the pwd.
Now, rather than use the pwd command... why not use the PWD environment variable?
For example:
ls > $PWD.txt
or
ls > ${PWD}.txt
is probably what you were trying to remember with your second example.
If you're in /home/hoboben and you want to create /home/hoboben/hoboben.txt, try:
ls > ${PWD}/${PWD##*/}.txt
If you do this, the file will contain its own name, so most often, you would remedy this in one of a few ways. You could redirect to somewhere else and move the file or name the file beginning with a dot to hide it from the ls command as long as you don't use the -a flag (and then optionally rename the resulting file).
I write my own scripts to manage a directory hierarchy of music files and I use subdirectories named ".info", for example, to contain track data in some spare files (basically, I "hide" metadata this way). It works out okay because my needs are simple and my collection small.

I suspect the problem may be that there are spaces in one of the directory names. For example, if your working directory is "/home/user/music/artist name". Bash will be confused thinking that you are trying to redirect to /home/user/music/artist and name.txt. You can fix this with double quotes
ls > "$(pwd).txt"
Also, you may not want to redirect to $(pwd).txt. In the example above, you would be redirecting the output to the file "/home/user/music/artist name.txt"

The syntax is:
ls > `pwd`.txt
That is the '`' character up underneath the '~', not the regular single quote.

Using the above method will create the files one level above your current directory. If you want the play lists to all go to one directory you'd need to do something like:
#!/bin/sh
MYVAR=`pwd | sed "s|/|_|g"`
ls > /playlistdir/$MYVAR-list.txt

to strip all but the directory name
ls >/playlistdir/${PWD##/*}.txt
this is probably not what you want because then you don't know where the files are (unless you change the ls command)
to replace "/" with "_"
ls >/playlistdir/${PWD//\//_}.txt
but then the playlist would look ugly and maybe not even fit in the selection window
So this will give you both a short readable name and usable paths inside the file
ext=.mp3 #leave blank for all files
for FILE in "$PWD/*$ext"; do echo "$FILE";done >/playlistdir/${PWD##/*}.txt

Related

Copying a list of files with wildcards into a new folder

I don't have much experience with the command line, but essentially I have a list of files in a single folder as follows:
file1_a_1
file1_a_2
file2_b_1
file2_b_2
file3_c_1
file3_c_2
And I also have a text file with the files I want. However, this list does not have the full file path, instead, it looks like this:
file1_a file3_c
because I want to move all files that start with 30 or so specific codes (i.e. everything that starts with file1_a and file1_c for all the files that start with this).
I have tried:
cp file1_a* file3_c* 'dir/dest'
but this does not work. I have also tried the find command. I think I have to use a loop to do this but I cannot find any help on looping through files with a wildcard on the end.
Thanks in advance! I am working on a linux machine in bash.
you can use the xargs command with find command and a pipe
find / -name xxxxx | xargs cp /..

Using functions as an argument in Bash

I want to move a couple of files from point a to point b
but I have to manually specify
mv /full/path/from/a /full/path/to/b
but some times there are 20 files which I have to move manually. Instead of /full/path/form/a, can't I just enter the a function which returns all the files which I want to move in my case;
/full/path/to/b is a directory, it's the target directory which all the files with extenstions mp3, exe and mp4 must go to:
mv ls *.{mp3,exe,mp4} /full/path/to/b
If I have to move a couple of files and I don't want to do it one by one, how can I optimize the problem?
The command mv ls *.{mp3,exe,mp4} /full/path/to/b in your question is not correct.
As pointed out in comments by #janos, the correct command is
mv *.{mp3,exe,mp4} /full/path/to/b
mv can complain about missing file if the file is really missing and/or the path is not accessible or is not valid.
As i can understand by your question description, if you go manually to the source path you can move the file to the desired directory.
Thus it seems that path is valid, and file exists.
In order mv to keeps complaining about *.mp3 not found (having a valid path and file) the only reason that pops up in my head is the Bash Pathname Expansion feature (enabled by default in my Debian).
Maybe for some reason this pathname expansion bash feature is disabled in your machine.
Try to enable this feature using command bellow and provide the correct command to mv and you should be fine.
$ set +f
PS: Check man bash about pathname expansion.

OS X bash For loop only processes one file in a directory

I'm trying to get this code to process all files in a directory : https://github.com/kieranjol/ifi-ffv1/blob/master/ifi-ffv1.sh
I run it in the terminal and add path to file ./ifi-ffv1.sh /path/to/file.mov. How can I get it to move on to the next? I'll also need to make sure that it only processes AV files, such as .avi/.mkv/*.mov etc.
I've tried using while loops with shift but I can't get that to work either.
I've tried adding a specific path like here but I'm failing http://www.cyberciti.biz/faq/unix-loop-through-files-in-a-directory/
I've tried this https://askubuntu.com/a/315338 and it keeps looping the same file rather than moving on to the next one. http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-7.html this didn't help me either.
I know this is going to be a horribly simple solution but I'm very new to this.
You don't actually have any kind of loop in your code. You need to do something like
for file in path/to/*.avi path/to/*.avg
do
./ifi-ffv1.sh "$file"
done
which will loop through all the specified files and substitute each one for $1
You can put whatever file names you want instead of the path/to/*.avi path/to/*.avg. If you cd to the directory first, you can leave out the paths, and just use *.avi *.avg
To do it all in one script, do something like this:
cd <your directory>
for file in *.avi *.avg
do
<your existing script here>
done
replacing all the $1's in your script with "$file" (not duplicating any quotes you already have, of course)

Copying multiple files with same name in the same folder terminal script

I have a lot of files named the same, with a directory structure (simplified) like this:
../foo1/bar1/dir/file_1.ps
../foo1/bar2/dir/file_1.ps
../foo2/bar1/dir/file_1.ps
.... and many more
As it is extremely inefficient to view all of those ps files by going to the
respective directory, I'd like to copy all of them into another directory, but include
the name of the first two directories (which are those relevant to my purpose) in the
file name.
I have previously tried like this, but I cannot get which file is from where, as they
are all named consecutively:
#!/bin/bash -xv
cp -v --backup=numbered {} */*/dir/file* ../plots/;
Where ../plots is the folder where I copy them. However, they are now of the form file.ps.~x~ (x is a number) so I get rid of the ".ps.~*~" and leave only the ps extension with:
rename 's/\.ps.~*~//g' *;
rename 's/\~/.ps/g' *;
Then, as the ps files have hundreds of points sometimes and take a long time to open, I just transform them into jpg.
for file in * ; do convert -density 150 -quality 70 "$file" "${file/.ps/}".jpg; done;
This is not really a working bash script as I have to change the directory manually.
I guess the best way to do it is to copy the files form the beginning with the names
of the first two directories incorporated in the copied filename.
How can I do this last thing?
If you just have two levels of directories, you can use
for file in */*/*.ps
do
ln "$file" "${file//\//_}"
done
This goes over each ps file, and hard links them to the current directory with the /s replaced by _. Use cp instead of ln if you intend to edit the files but don't want to update the originals.
For arbitrary directory levels, you can use the bash specific
shopt -s globstar
for file in **/*.ps
do
ln "$file" "${file//\//_}"
done
But are you sure you need to copy them all to one directory? You might be able to open them all with yourreader */*/*.ps, which depending on your reader may let browse through them one by one while still seeing the full path.
You should run a find command and print the names first like
find . -name "file_1.ps" -print
Then iterate over each of them and do a string replacement of / to '-' or any other character like
${filename/\//-}
The general syntax is ${string/substring/replacement}. Then you can copy it to the required directory. The complete script can be written as follows. Haven't tested it (not on linux at the moment), so you might need to tweak the code if you get any syntax error ;)
for filename in `find . -name "file_1.ps" -print`
do
newFileName=${filename/\//-}
cp $filename YourNewDirectory/$newFileName
done
You will need to place the script in the same root directory or change the find command to look for the particular directory if you are placing the above script in some other directory.
References
string manipulation in bash
find man page

How can I display a text file within a bash script?

Im trying to display a text file in a directory within the folder my script is in. I tried things like:
mypath=`realpath $0`
FILE="$realpath/Folder/Text.txt"
cat $FILE
And
FILE="$PWD/Folder/Text.txt"
cat $FILE
but they include the name of the file instead of just the its running from. I also want it to work with symbolic links.
cat "$(dirname -- "$0")/Folder/Text.txt"
That is, send the file Text.txt in the directory Folder below the directory where this script is located to standard output.
This will work with symlinks. And yes, all the quotes are significant.
You can do:
cat "./Folder/Text.txt"
the period denotes the current folder you are in. a double period "../Folder/Text.txt" denotes a folder up in the directory tree.

Resources