weird behaviour of If statement in (t)csh - if-statement

I have an if loop that's not quite doing what it's supposed to.
I want the if loop to look for a particular file "if (-f JUN*[0-9].acc$RUN.nc)" and if it finds it, continue doing the indented things.
If not, it should jump down past the endif and keep reading through the script.
At the moment, it's looking for the file ok (it has been able to go through the first "if" statement and do the right things when it finds the first file), but as soon as it doesn't find what it's looking for, it stops the whole script and returns the error:
DEC*[0-9].accE01Ccek0kA.nc: No match.
I've tried various combinations of if: ... else: pass and if ... then ... endif with colons and such in different places, but I still can't get it working.
Thanks for any help!
setenv RUN $1 # Run number
setenv BDIR /discover/nobackup/cekrause
setenv MONDATA $BDIR/$RUN/data_files # Target directory
setenv DATADIR $BDIR/$RUN # Run directory
setenv EXECDIR /discover/nobackup/projects/giss/exec
cd $DATADIR
#### pdE JUN and DEC files ####
if (-f JUN*[0-9].acc$RUN.nc) then
mkdir tempplot
cp JUN*[0-9].acc$RUN.nc tempplot
cd tempplot
pdE JUN*[0-9].acc$RUN.nc
endif
cd $DATADIR
if (-f DEC*[0-9].acc$RUN.nc) then
mkdir tempplot
cp DEC*[0-9].acc$RUN.nc tempplot
cd tempplot
pdE DEC*[0-9].acc$RUN.nc
endif
(do some other things)
exit

You use csh or tcsh instead of bash, and the somewhat irrational behaviour that you just discovered is one of the reasons not to use csh for programming. And there are more of them. Much more. So, please: When you write a shell script, don't ever use (t)csh. Use bash, ksh, dash, zsh, or plain old sh; they are all much better suited for this task. Yes, the syntax is noticably different from csh, so you will have to rewrite parts of your script, but it pays off. Your script should look as follows:
#!/bin/bash
export RUN=$1 # Run number
export BDIR=/discover/nobackup/cekrause
export MONDATA=$BDIR/$RUN/data_files # Target directory
export DATADIR=$BDIR/$RUN # Run directory
export EXECDIR=/discover/nobackup/projects/giss/exec
cd "$DATADIR"
#### pdE JUN and DEC files ####
if [ -f JUN*[0-9].acc"$RUN".nc ] ; then # note spaces around [, ], and semicolon before "then"
mkdir tempplot
cp JUN*[0-9].acc"$RUN".nc tempplot
cd tempplot
pdE JUN*[0-9].acc"$RUN".nc
fi # bash uses "fi" instead of "endif"
cd "$DATADIR"
if [ -f DEC*[0-9].acc"$RUN".nc ] ; then
mkdir tempplot
cp DEC*[0-9].acc"$RUN".nc tempplot
cd tempplot
pdE DEC*[0-9].acc"$RUN".nc
fi
(do some other things)
exit

If is not written like that in bash. it should be something like:
if [ -f JUN*[0-9].acc$RUN.nc ] ; then
And yeah, please consult already available online resources on questions like these. It's not like there is no a single website with this stuff.

In csh if a glob such as DEC*[0-9].accE01Ccek0kA.nc fails to match a file then you get a No match error. This means you pretty much cannot use -f with a glob.
You are much better off using bash for scripting because of numerous problems like this. However, if you must use csh you can get past this particular quirk by including:
set nonomatch
before you use any globs.

Related

Bash script to change file extension using regex

I have a lot of files i've copied over from my iphone file system, to start with they were mp3 files, but app on iphone changed their names to some random staff which looks like:
1c03e04cc1bbfcb0c1237f57f1d0ae2e.mp3?extra=f7NhT68pNkmEbGA_I1WbVShXQ2E2gJAGBKSEyh3hf0hsbLB1cqnXDuepYA5ubcFm_B3KSsrXDuKVtWVAUh_MAPeFiEHXVdg
I only need to remove part of file name after mp3. Please give me a script - there are more than 600 files, and manually it is impossible.
you can use rename command:
rename "s/mp3\?.*/mp3/" *.mp3*
#!/bin/bash
shopt -s nullglob
for F in *.mp3\?*; do
echo mv -v -- "$F" "${F%%.mp3\?*}.mp3"
done
Save it to a script like script.sh then run as bash /path/to/script.sh in the directory where the files exist.
Remove echo when you find it correct already.

Bash: Moving Files with Regex that Matches Pattern of Script Arugment and Fixed Text

I'm trying to write a backup script that takes a directory and directory/file name as arguments to the script. The problem is curating the target directory of the backups. For safety, I'm currently moving the files into MacOS ~/.Trash/. The problem is that I want to support having spaces in the target directory's file name, but escaping the path in mv prevents shell expansion of *.
The script in question:
# Usage: backup-to-dropbox.sh "path/containing/target" "target dir"
cd "$1"
DATE=`date "+%Y%m%dT%H%M%S"`
SOURCE="$2"
mv "~/Dropbox/Backups/$SOURCE*.tgz" ~/.Trash/ ## Problem line here
tar -czf "$SOURCE $DATE.tgz" "$SOURCE/"
mv "$SOURCE $DATE.tgz" ~/Dropbox/Backups/
How can I match all the files with this known, arbitrary prefix and fixed extension?
Words can be partially quoted. Be sure not to quote anything expanded by the shell.
mv ~/"Dropbox/Backups/$SOURCE"*.tgz ~/.Trash/

Can't run a linkage script through VIM

Hello, I wrote a bash script that compile several cpp's and object files
in g++. My goal is to run the script in vim by :!, but
it doesn't works within vim, only when I'm outside.
In addition I wanted to why using % in a script
doesn't give me the current file, but gives an error
instead.
the script:
#!/bin/bash
# Search for the main module and remove the ext.
delimain=`grep main *.cpp | cut -d. -f1`
# Checks if there are also object files
if [ -f ./*.o ]; then
g++ -g *.cpp *.o -o $delimain.exe
# If There are only cpp file
else
g++ -g *.cpp -o $delimain.exe
fi
Thanks!
RE your comment
I'm using ubuntu 13.10 alias: alias link 'sh ~/bin/lin_script %'
You should not invoke a shell script with an explicit interpreter; the #!/bin/bash first line tells the shell already which interpreter to use. You're obviously a beginner in Bash; try to read some introductions to gain a better understanding.
Aliases won't work in Vim because they are only defined in an interactive shell, but the commands launched from Vim usually are launched in a non-interactive shell (because this is faster and comes with less unnecessary stuff).
The alias is interpreted by the shell, but the % symbol is special to Vim. The two are not the same. See my other answer how to pass a filename to the script.
You're right that % is automatically expanded when supplied to an Ex command inside Vim, but this does not apply to external scripts. What you have to do is pass the current file when invoking the external script, and in there reference the command-line argument:
Inside Vim:
:!linkage.sh %
In your script:
if [ $# -gt 0 ]; then
delimain=$1
else
delimain=`grep ...

Copy and Rename Multiple Files with Regular Expressions in bash

I've got a file structure that looks like:
A/
2098765.1ext
2098765.2ext
2098765.3ext
2098765.4ext
12345.1ext
12345.2ext
12345.3ext
12345.4ext
B/
2056789.1ext
2056789.2ext
2056789.3ext
2056789.4ext
54321.1ext
54321.2ext
54321.3ext
54321.4ext
I need to rename all the files that begin with 20 to start with 10; i.e., I need to rename B/2022222.1ext to B/1022222.1ext
I've seen many of the other questions regarding renaming multiple files, but couldn't seem to make it work for my case. Just to see if I can figure out what I'm doing before I actually try to do the copy/renaming I've done:
for file in "*/20?????.*"; do
echo "{$file/20/10}";
done
but all I get is
{*/20?????.*/20/10}
Can someone show me how to do this?
You just have a little bit of incorrect syntax is all:
for file in */20?????.*; do mv $file ${file/20/10}; done
Remove quotes from the argument to in. Otherwise, the filename expansion does not occur.
The $ in the substitution should go before the bracket
Here is a solution which use the find command:
find . -name '20*' | while read oldname; do echo mv "$oldname" "${oldname/20/10}"; done
This command does not actually do your bidding, it only prints out what should be done. Review the output and if you are happy, remove the echo command and run it for real.
Just wanna add to Explosion Pill's answer.
On OS X though, you must say
mv "${file}" "${file_expression}"
Or the mv command does not recognize it.
Brace expansions like :
{*/20?????.*/20/10}
can't be surrounded by quotes.
Instead, try doing (with Perl rename) :
rename 's/^10/^20/' */*.ext
You can do this using the Perl tool rename from the shell prompt. (There are other tools with the same name which may or may not be able to do this, so be careful.)
If you want to do a dry run to make sure you don't clobber any files, add the -n switch to the command.
note
If you run the following command (linux)
$ file $(readlink -f $(type -p rename))
and you have a result like
.../rename: Perl script, ASCII text executable
then this seems to be the right tool =)
This seems to be the default rename command on Ubuntu.
To make it the default on Debian and derivative like Ubuntu :
sudo update-alternatives --set rename /path/to/rename
The glob behavior of * is suppressed in double quotes. Try:
for file in */20?????.*; do
echo "${file/20/10}";
done

Is there a "watch" / "monitor" / "guard" program for Makefile dependencies?

I've recently been spoiled by using nodemon in a terminal window, to run my Node.js program whenever I save a change.
I would like to do something similar with some C++ code I have. My actual project has lots of source files, but if we assume the following example, I would like to run make automatically whenever I save a change to sample.dat, program.c or header.h.
test: program sample.dat
./program < sample.dat
program: program.c header.h
gcc program.c -o program
Is there an existing solution which does this?
(Without firing up an IDE. I know lots of IDEs can do a project rebuild when you change files.)
If you are on a platform that supports inotifywait (to my knowledge, only Linux; but since you asked about Make, it seems there's a good chance you're on Linux; for OS X, see this question), you can do something like this:
inotifywait --exclude '.*\.swp|.*\.o|.*~' --event MODIFY -q -m -r . |
while read
do make
done
Breaking that down:
inotifywait
Listen for file system events.
--exclude '.*\.swp|.*\.o|.*~'
Exclude files that end in .swp, .o or ~ (you'll probably want to add to this list).
--event MODIFY
When you find one print out the filepath of the file for which the event occurred.
-q
Do not print startup messages (so make is not prematurely invoked).
-m
Listen continuously.
-r .
Listen recursively on the current directory. Then it is piped into a simple loop which invokes make for every line read.
Tailor it to your needs. You may find inotifywait --help and the manpage helpful.
Here is a more detailed script. I haven't tested it much, so use with discernment. It is meant to keep the build from happening again and again needlessly, such as when switching branches in Git.
#!/bin/sh
datestampFormat="%Y%m%d%H%M%S"
lastrun=$(date +$datestampFormat)
inotifywait --exclude '.*\.swp|.*\.o|.*~' \
--event MODIFY \
--timefmt $datestampFormat \
--format %T \
-q -m -r . |
while read modified; do
if [ $modified -gt $lastrun ]; then
make
lastrun=$(date +$datestampFormat)
fi
done