Bash script 'mv -f' embedded in Perl file - bash

Attempting to move directory which contains sub-directory to the /opt using perl. Just perl.
#!/usr/bin/perl -w
use strict;
use warning
system(
'echo "Copy the application directory here and paste it here"
read apple
mv -f $apple /opt
');
This returns
mv: cannot stat "'/root/dump'": No such file or directory
My input for read apple is /root/dump which is a directory. When I did it directly from terminal it worked but then by perl file it doesn't. Could you be descriptive as possible, as I'm not that familiar with bash script please? Thank you in Advance.
Update:
I tried the bash file I wrote
#!/bin/bash
echo "Copy the application directory here and paste it here"
read apple
mv -f $apple /opt
This also returns the same result as perl.

If it literally says "'/root/dump'" that means you have added some literal quotes in addition to the syntactic ones. Make sure to quote $apple in your Bash script and not quote the stuff you give to read.

If you want to use just perl, instead of system()ing out to a shell, use dirmove() from File::Copy::Recursive to move the directory tree.

If I understand your question, you just want to move a directory recoursively to /opt. This script does just that.
#!/usr/bin/perl -w
use strict;
print "Copy the application directory here and paste it here: ";
chomp (my $apple = <STDIN>);
`mv -f $apple /opt`;
using perl. Just perl.
I don't understand that. You are using system commands in your code provided above. If you want to use just perl use dirmove() from File::Copy::Recursive as Shawn mentioned already. Hope this helps.

Related

In Solaris, I need to move a file, name of which is in a filelist

This is in Solaris. Please note, i am a novice in shell scripting. So request for some assistance.
I have a filelist with a filename(just a single filename). I need to move the file from one directory to another directory, by reading this filelist.
I need a command to do it.
To add something like below, where filelist.txt contains a filename and the file is in the source path /path and i need to move it to a different path /path2/dest
mv /path/ 'cat filelist.txt' /path2/dest/
You're close. Instead of 'cat filelist.txt, you need to make it a subshell. Assuming you're sh compatible, this should work:
mv /path/$(cat filelist.txt) /path2/dest/
If you want to support multiple files however, a simple loop would do the trick.
cat filelist.txt | while read f; do
mv /path/${f} /path2/dest
done

One-line way to remove last n characters from string

I have a bunch of files in the format photo.jpg.png in a folder, and for every photo in this folder, I want to replace the .jpg.png with .png. How can I do this from Terminal?
I have a basic Python and bash background, so I know I'd want to do something like this:
$ for i in *.png; do mv $i $i[:-8]; done
$ for i in *; do mv $i $i.png; done
But what would I replace the Pythonic [:-8] with in order to remove the last 8 characters of each filename?
EDIT I now realize that a substring that counts from the end of the string would be superior. Is there a way to do this as well?
You can use pattern expansion:
for f in *.jpg.png; do mv -v "$f" "${f/.jpg.png/.png}"; done
Though you might still have problems with a filename like foo.jpg.png.gif.
If you really want to strip the last 7 or 8 characters, you can use a substring expansion:
for f in *.jpg.png; do mv -v "$f" "${f:0:-7}png"; done
Note that use of negative numbers in substring length requires bash version 4 or higher.
With Perl's standalone rename command:
rename -n 's/jpg\.png$/png/' *.jpg.png
or
rename -n 's/.......$/png/' *.jpg.png
Output:
rename(photo.jpg.png, photo.jpg)
If everything looks okay, remove `-n'.
rename is designed for this kinda thing;
$ rename .jpg.png .png *.jpg.png
For MacOS, I realized that rename may not be available by default, you can install it using brew.
$ brew install rename
and then use -s option for rename;
$ rename -s .jpg.png .png *.jpg.png
Removing the last 8 characters using perl's rename :
$ rename -n 's/.{8}$//' *.png
(remove -n switch when your tests are OK)
or with bash :
for i in *.png; do
echo mv "$i" "${i:0:-8}"
done
(remove echo when your tests are OK)
There are other tools with the same name which may or may not be able to do this, so be careful.
If you run the following command (GNU)
$ file "$(readlink -f "$(type -p rename)")"
and you have a result like
.../rename: Perl script, ASCII text executable
and not containing:
ELF
then this seems to be the right tool =)
If not, to make it the default (usually already the case) on Debian and derivative like Ubuntu :
$ sudo update-alternatives --set rename /path/to/rename
(replace /path/to/rename to the path of your perl's rename command.
If you don't have this command, search your package manager to install it or do it manually
Last but not least, this tool was originally written by Larry Wall, the Perl's dad.
Something like that:
$ v=test.jpg.png
$ echo ${v:0:-8}
test
This should work:
for file in *.jpg.png; do mv $file ${file//jpg.png/jpg} ; done

Faster way to Unix cd by changing one directory inside path?

I have multiple directories (eg tom richard harry) that have identical subdirectory and file structure. If I am working on a file inside one directory, is there a fast or easy way to cd to the equivalent path in another directory?
Example
pwd=/mystuff/myproject/tom/hobbies/sports/highschool
cd /mystuff/myproject/richard/hobbies/sports/highschool
I was hoping for some shortcut like cd pwd but change tom > richard in one command.
The following should work:
cd ${PWD/tom/richard}
This would work:
cd $(pwd | perl -pi -e 's/tom/richard/g;')
If you know what directory you're in (say stored in $dirname variable):
function dirswitch() {
newdir="$1"
cd $(pwd | sed -e "s#/$dirname/#/$newdir/#")
}
This should handle the job in bash. So if you're in dirname=tom and you want to switch to harry:
dirswitch harry
...will do the trick.
You can use bash's history expansion for this.
^tom^richard - this will rerun the previous command, substituting richard for tom.
Bash History Expansion
It all depends upon your shell...
Most people use BASH -- it's the standard Linux shell, but Kornshell is very similar to BASH, and has the feature you're looking for:
$ cd /mystuff/myproject/tom/hobbies/sports/highschool
$ cd tom richard
$ pwd
/mystuff/myproject/richard/hobbies/sports/highschool
I also like the Kornshell print command and the way variables in Kornshell don't disappear on you in loops (because BASH makes them child processes).
Of course, BASH has features that are missing in Kornshell. One example is setting your prompt. In Bash, I set my prompt as thus:
PS1="\u#\h:\w\n\$ "
\u is the user ID
\h is the short host name
\w is the working directory in relationship to $HOME
\n is the newline
\$ is a $ if your ID isn't root and # if your ID is root.
The Kornshell equivalent is:
PS1=$(print -n "logname#hostname:";if [[ "${PWD#$HOME}" != "$PWD" ]] then; print -n "~${PWD#$HOME}"; else; print -n "$PWD";fi;print "\n$ ")
As I said, they're mostly equivalent. I can work with either one, but Kornshell has this particular feature and BASH doesn't.
Your alternative is to write a function that will do this for you, or to make an alias to the cd command.
Some shells, such as Zsh and ksh offer a special form of the cd builtin:
cd [ -qsLP ] old new
The second form of cd substitutes the string new for the string old in the
name of the current directory, and tries to change to this new directory.
So if you are using zsh or ksh, then this command should do it:
cd /mystuff/myproject/tom /mystuff/myproject/richard
no matter which subdirectory of /mystuff/myproject/tom you happen to currently be in.

Shell script not running, command not found

I am very, very new to UNIX programming (running on MacOSX Mountain Lion via Terminal). I've been learning the basics from a bioinformatics and molecular methods course (we've had two classes) where we will eventually be using perl and python for data management purposes. Anyway, we have been tasked with writing a shell script to take data from a group of files and write it to a new file in a format that can be read by a specific program (Migrate-N).
I have gotten a number of functions to do exactly what I need independently when I type them into the command line, but when I put them all together in a script and try to run it I get an error. Here are the details (I apologize for the length):
#! /bin/bash
grep -f Samples.NFCup.txt locus1.fasta > locus1.NFCup.txt
grep -f Samples.NFCup.txt locus2.fasta > locus2.NFCup.txt
grep -f Samples.NFCup.txt locus3.fasta > locus3.NFCup.txt
grep -f Samples.NFCup.txt locus4.fasta > locus4.NFCup.txt
grep -f Samples.NFCup.txt locus5.fasta > locus5.NFCup.txt
grep -f Samples.Salmon.txt locus1.fasta > locus1.Salmon.txt
grep -f Samples.Salmon.txt locus2.fasta > locus2.Salmon.txt
grep -f Samples.Salmon.txt locus3.fasta > locus3.Salmon.txt
grep -f Samples.Salmon.txt locus4.fasta > locus4.Salmon.txt
grep -f Samples.Salmon.txt locus5.fasta > locus5.Salmon.txt
grep -f Samples.Cascades.txt locus1.fasta > locus1.Cascades.txt
grep -f Samples.Cascades.txt locus2.fasta > locus2.Cascades.txt
grep -f Samples.Cascades.txt locus3.fasta > locus3.Cascades.txt
grep -f Samples.Cascades.txt locus4.fasta > locus4.Cascades.txt
grep -f Samples.Cascades.txt locus5.fasta > locus5.Cascades.txt
echo 3 5 Salex_melanopsis > Smelanopsis.mig
echo 656 708 847 1159 779 >> Smelanopsis.mig
echo 154 124 120 74 126 NFCup >> Smelanopsis.mig
cat locus1.NFCup.txt locus2.NFCup.txt locus3.NFCup.txt locus4.NFCup.txt locus5.NFCup.txt >> Smelanopsis.mig
echo 32 30 30 18 38 Salmon River >> Smelanopsis.mig
cat locus1.Salmon.txt locus2.Salmon.txt locus3.Salmon.txt locus4.Salmon.txt locus5.Salmon.txt >> Smelanopsis.mig
echo 56 52 24 29 48 Cascades >> Smelanopsis.mig
cat locus1.Cascades.txt locus2.Cascades.txt locus3.Cascades.txt locus4.Cascades.txt locus5.Cascades.txt >> Smelanopsis.mig
The series of greps are just pulling out DNA sequence data for each site for each locus into new text files. The Samples...txt files have the sample ID numbers for a site, the .fasta files have the sequence information organized by sample ID; the grepping works just fine in command line if I run it individually.
The second group of code creates the actual new file I need to end up with, that ends in .mig. The echo lines are data about counts (basepairs per locus, populations in the analysis, samples per site, etc.) that the program needs information on. The cat lines are to mash together the locus by site data created by all the grepping below the site-specific information dictated in the echo line. You no doubt get the picture.
For creating the shell script I've been starting in Excel so I can easily copy-paste/autofill cells, saving as tab-delimited text, then opening that text file in TextWrangler to remove the tabs before saving as a .sh file (Line breaks: Unix (LF) and Encoding: Unicode (UTF-8)) in the same directory as all the files used in the script. I've tried using chmod +x FILENAME.sh and chmod u+x FILENAME.sh to try to make sure it is executable, but to no avail. Even if I cut the script down to just a single grep line (with the #! /bin/bash first line) I can't get it to work. The process only takes a moment when I type it directly into the command line as none of these files are larger than 160KB and some are significantly smaller. This is what I type in and what I get when I try to run the file (HW is the correct directory)
localhost:HW Mirel$ MigrateNshell.sh
-bash: MigrateNshell.sh: command not found
I've been at this impass for two days now, so any input would be greatly appreciated! Thanks!!
For security reasons, the shell will not search the current directory (by default) for an executable. You have to be specific, and tell bash that your script is in the current directory (.):
$ ./MigrateNshell.sh
Change the first line to the following as pointed out by Marc B
#!/bin/bash
Then mark the script as executable and execute it from the command line
chmod +x MigrateNshell.sh
./MigrateNshell.sh
or simply execute bash from the command line passing in your script as a parameter
/bin/bash MigrateNshell.sh
Make sure you are not using "PATH" as a variable, which will override the existing PATH for environment variables.
Also try to dos2unix the shell script, because sometimes it has Windows line endings and the shell does not recognize it.
$ dos2unix MigrateNshell.sh
This helps sometimes.
#! /bin/bash
^---
remove the indicated space. The shebang should be
#!/bin/bash
Unix has a variable called PATH that is a list of directories where to find commands.
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/Users/david/bin
If I type a command foo at the command line, my shell will first see if there's an executable command /usr/local/bin/foo. If there is, it will execute /usr/local/bin/foo. If not, it will see if there's an executable command /usr/bin/foo and if not there, it will look to see if /bin/foo exists, etc. until it gets to /Users/david/bin/foo.
If it can't find a command foo in any of those directories, it tell me command not found.
There are several ways I can handle this issue:
Use the commandbash foo since foo is a shell script.
Include the directory name when you eecute the command like /Users/david/foo or $PWD/foo or just plain ./foo.
Change your $PATH variable to add the directory that contains your commands to the PATH.
You can modify $HOME/.bash_profile or $HOME/.profile if .bash_profile doesn't exist. I did that to add in /usr/local/bin which I placed first in my path. This way, I can override the standard commands that are in the OS. For example, I have Ant 1.9.1, but the Mac came with Ant 1.8.4. I put my ant command in /usr/local/bin, so my version of antwill execute first. I also added $HOME/bin to the end of the PATH for my own commands. If I had a file like the one you want to execute, I'll place it in $HOME/bin to execute it.
Try chmod u+x MigrateNshell.sh
There have been a few good comments about adding the shebang line to the beginning of the script. I'd like to add a recommendation to use the env command as well, for additional portability.
While #!/bin/bash may be the correct location on your system, that's not universal. Additionally, that may not be the user's preferred bash. #!/usr/bin/env bash will select the first bash found in the path.
Also make sure /bin/bash is the proper location for bash .... if you took that line from an example somewhere it may not match your particular server. If you are specifying an invalid location for bash you're going to have a problem.
Add below lines in your .profile path
PATH=$PATH:$HOME/bin:$Dir_where_script_exists
export PATH
Now your script should work without ./
Raj Dagla
I'm new to shell scripting too, but I had this same issue. Make sure at the end of your script you have a blank line. Otherwise it won't work.
First:
chmod 777 ./MigrateNshell.sh
Then:
./MigrateNshell.sh
Or, add your program to a directory recognized in your $PATH variable. Example: Path Variable Example
Which will then allow you to call your program without ./

Using bash command line, how to add "import package.name.*;" to many java files?

I'm thinking of using find or grep to collect the files, and maybe sed to make the change, but what to do with the output? Or would it be better to use "argdo" in vim?
Note: this question is asking for command line solutions, not IDE's. Answers and comments suggesting IDE's will be calmly, politely and serenely flagged. :-)
I am huge fan of the following
export MYLIST=`find . -type f -name *.java`
for a in $MYLIST; do
mv $a $a.orig
echo "import.stuff" >> $a
cat $a.orig >> $a
chmod 755 $a
done;
mv is evil and eventually this will get you. But I use this same construct for a lot of things and it is my utility knife of choice.
Update: This method also backs up the files which you should do using any method. In addition it does not use anything but the shell's features. You don't have to jog your memory about tools you don't use often. It is simple enough to teach a monkey (and believe me I have) to do. And you are generally wise enough to just throw it away because it took four seconds to write.
you can use sed to insert a line before the first line of the file:
sed -ie "1i import package.name.*;" YourClass.java
use a for loop to iterate through all your files and run this expression on them. but be careful if you have packages, because the import statements must be after the package declaration. you can use a more complex sed expression, if that's the case.
I'd suggest sed -i to obviate the need to worry about the output. Since you don't specify your platform, check your man pages; the semantics of sed -i vary from Linux to BSD.
I would use sed if there was a decent way to so "do this for the first line only" but I don't know of one off of the top of my head. Why not use perl instead. Something like:
find . -name '*.java' -exec perl -p -i.bak -e '
BEGIN {
print "import package.name.*;\n"
}' {} \;
should do the job. Check perlrun(1) for more details.
for i in `ls *java`
do
sed -i '.old' '1 i\
Your include statement here.
' $i
done
Should do it. -i does an in place replacement and .old saves the old file just in case something goes wrong. Replace the iterator *java as necessary (maybe 'find . | grep java' or something instead.)
You may also use the ed command to do in-file search and replace:
# delete all lines matching foobar
ed -s test.txt <<< $'g/foobar/d\nw'
see: http://bash-hackers.org/wiki/doku.php?id=howto:edit-ed
I've actually starting to do it using "argdo" in vim. First of all, set the args:
:args **/*.java
The "**" traverses all the subdir, and the "args" sets them to be the arg list (as if you started vim with all those files in as arguments to vim, eg: vim package1/One.java package1/Two.java package2/One.java)
Then fiddle with whatever commands I need to make the transform I want, eg:
:/^package.*$/s/$/\rimport package.name.*;/
The "/^package.*$/" acts as an address for the ordinary "s///" substitution that follows it; the "/$/" matches the end of the package's line; the "\r" is to get a newline.
Now I can automate this over all files, with argdo. I hit ":", then uparrow to get the above line, then insert "argdo " so it becomes:
:argdo /^package.*$/s/$/\rimport package.name.*;/
This "argdo" applies that transform to each file in the argument list.
What is really nice about this solution is that it isn't dangerous: it hasn't actually changed the files yet, but I can look at them to confirm it did what I wanted. I can undo on specific files, or I can exit if I don't like what it's done (BTW: I've mapped ^n and ^p to :n and :N so I can scoot quickly through the files). Now, I commit them with ":wa" - "write all" files.
:wa
At this point, I can still undo specific files, or finesse them as needed.
This same approach can be used for other refactorings (e.g. change a method signature and calls to it, in many files).
BTW: This is clumsy: "s/$/\rtext/"... There must be a better way to append text from vim's commandline...

Resources