Opening Solution file in Cygwin / Bash - bash

I'm trying to set up a simple bash alias that will open a solution file in the current directory via cygstart
I'm unsuccessfully trying something like
ls | grep '.sln' | cygstart $0
I'm no expert in bash commands and was wondering what the correct command is?

You want to try:
ls *.sln | xargs cygstart
or better:
find . -iname "*.sln" -print | xargs cygstart

Related

xargs: ggrep: no such file or directory

I use the following command in Git Bash to search for Java classes using non ASCII characters:
find */src/*/java -name "*.java" -print0 | xargs -0 ggrep -lP "[\x80-\xFF]"
I get the error:
xargs: ggrep: no such file or directory
Any other way to run this command in Windows 7?

Passing commands to a subshell with xargs does not work inside an alias

I was trying to build a quick script to find all the git repos under a directory and sequentially "git pull" each one.
This is what I found so far:
find ~/ -name ".git" -type d | sed 's,/*[^/]\+/*$,,' | xargs -L1 bash -c 'cd "$1" && git pull' _
If pasted into a terminal, this will work exactly as it's intended. However, if I make this into an alias in my .bashrc file:
alias gpa="find ~/ -name ".git" -type d | sed 's,/*[^/]\+/*$,,' | xargs -L1 bash -c 'cd "$1" && git pull' _"
The command doesn't work. I modified it in an attempt to get it to print what the subshell launched by xargs is receiving:
alias printgpa="find ~/ -name ".git" -type d | sed 's,/*[^/]\+/*$,,' | xargs -L1 bash -c 'echo "$1"' _"
When run, each subshell prints a newline, but nothing else.
Can anyone answer why this is happening? My gut feeling says that it's a problem with my syntax in the alias, but I don't know exactly what's going on.
The problem is that $1 is getting substituted when you define the alias, rather than when you run it. To prevent that, you need to quote the $, either by using a backslash, or by using some single-quotes. For example:
alias printgpa='find ~/ -name .git -type d | sed '\''s,/*[^/]\+/*$,,'\'' | xargs -L1 bash -c '\''echo "$1"'\'' _'
Maybe it is easier to read when you use GNU Parallel:
alias gpa="find ~/ -name .git -type d | parallel 'cd {//} && git pull'"
Plus you get the added benefit of getting more gits to pull in parallel.
It takes literally 10 seconds to install GNU Parallel:
wget pi.dk/3 -qO - | sh -x
Watch the intro videos to learn more: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Renaming multiple files in one line of shell

Problem
In a directory there are files of the format: *-foo-bar.txt.
Example directory:
$ ls *-*
asdf-foo-bar.txt ghjk-foo-bar.txt l-foo-bar.txt tyui-foo-bar.txt
bnm-foo-bar.txt iop-foo-bar.txt qwer-foo-bar.txt zxcv-foo-bar.txt
Desired directory:
$ ls *.txt
asdf.txt bnm.txt ghjk.txt iop.txt l.txt qwer.txt tyui.txt zxcv.txt
Solution 1
The first solution that came to my mind looks somewhat like this ugly hack:
ls *-* | cut -d- -f1 | sed 's/.*/mv "\0-foo-bar.txt" "\0.txt"/' > rename.sh && sh rename.sh
The above solution creates a script, on the fly, to rename the files. This solution also tries to parse the output of ls which is not a good thing to do as per http://mywiki.wooledge.org/ParsingLs.
Solution 2
This problem can be solved more elegantly with a shell script like this:
for i in *-*
do
mv "$i" "`echo $i | cut -f1 -d-`.txt"
done
The above solution uses a loop to rename the files.
Question
Is there a way to solve this problem in a single line such that we do not have to explicitly script a loop, or generate a script, or invoke a new or the current shell (i.e. avoid sh, bash, ., etc. commands)?
Have you tried the rename command?
For example:
rename 's/-foo-bar//' *-foo-bar.txt
If you don't have that available, I would use find, sed, and xargs:
find . -maxdepth 1 -mindepth 1 -type f -name '*-foo-bar.txt' | sed 's/-foo-bar.txt//' | xargs -I{} mv {}-foo-bar.txt {}.txt

Modifying replace string in xargs

When I am using xargs sometimes I do not need to explicitly use the replacing string:
find . -name "*.txt" | xargs rm -rf
In other cases, I want to specify the replacing string in order to do things like:
find . -name "*.txt" | xargs -I '{}' mv '{}' /foo/'{}'.bar
The previous command would move all the text files under the current directory into /foo and it will append the extension bar to all the files.
If instead of appending some text to the replace string, I wanted to modify that string such that I could insert some text between the name and extension of the files, how could I do that? For instance, let's say I want to do the same as in the previous example, but the files should be renamed/moved from <name>.txt to /foo/<name>.bar.txt (instead of /foo/<name>.txt.bar).
UPDATE: I manage to find a solution:
find . -name "*.txt" | xargs -I{} \
sh -c 'base=$(basename $1) ; name=${base%.*} ; ext=${base##*.} ; \
mv "$1" "foo/${name}.bar.${ext}"' -- {}
But I wonder if there is a shorter/better solution.
The following command constructs the move command with xargs, replaces the second occurrence of '.' with '.bar.', then executes the commands with bash, working on mac OSX.
ls *.txt | xargs -I {} echo mv {} foo/{} | sed 's/\./.bar./2' | bash
It is possible to do this in one pass (tested in GNU) avoiding the use of the temporary variable assignments
find . -name "*.txt" | xargs -I{} sh -c 'mv "$1" "foo/$(basename ${1%.*}).new.${1##*.}"' -- {}
In cases like this, a while loop would be more readable:
find . -name "*.txt" | while IFS= read -r pathname; do
base=$(basename "$pathname"); name=${base%.*}; ext=${base##*.}
mv "$pathname" "foo/${name}.bar.${ext}"
done
Note that you may find files with the same name in different subdirectories. Are you OK with duplicates being over-written by mv?
If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you can do this:
find . -name "*.txt" | parallel 'ext={/} ; mv -- {} foo/{/.}.bar."${ext##*.}"'
Watch the intro videos for GNU Parallel to learn more:
https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
If you're allowed to use something other than bash/sh, AND this is just for a fancy "mv"... you might try the venerable "rename.pl" script. I use it on Linux and cygwin on windows all the time.
http://people.sc.fsu.edu/~jburkardt/pl_src/rename/rename.html
rename.pl 's/^(.*?)\.(.*)$/\1-new_stuff_here.\2/' list_of_files_or_glob
You can also use a "-p" parameter to rename.pl to have it tell you what it WOULD HAVE DONE, without actually doing it.
I just tried the following in my c:/bin (cygwin/windows environment). I used the "-p" so it spit out what it would have done. This example just splits the base and extension, and adds a string in between them.
perl c:/bin/rename.pl -p 's/^(.*?)\.(.*)$/\1-new_stuff_here.\2/' *.bat
rename "here.bat" => "here-new_stuff_here.bat"
rename "htmldecode.bat" => "htmldecode-new_stuff_here.bat"
rename "htmlencode.bat" => "htmlencode-new_stuff_here.bat"
rename "sdiff.bat" => "sdiff-new_stuff_here.bat"
rename "widvars.bat" => "widvars-new_stuff_here.bat"
the files should be renamed/moved from <name>.txt to /foo/<name>.bar.txt
You can use rename utility, e.g.:
rename s/\.txt$/\.txt\.bar/g *.txt
Hint: The subsitution syntax is similar to sed or vim.
Then move the files to some target directory by using mv:
mkdir /some/path
mv *.bar /some/path
To do rename files into subdirectories based on some part of their name, check for:
-p/--mkpath/--make-dirs Create any non-existent directories in the target path.
Testing:
$ touch {1..5}.txt
$ rename --dry-run "s/.txt$/.txt.bar/g" *.txt
'1.txt' would be renamed to '1.txt.bar'
'2.txt' would be renamed to '2.txt.bar'
'3.txt' would be renamed to '3.txt.bar'
'4.txt' would be renamed to '4.txt.bar'
'5.txt' would be renamed to '5.txt.bar'
Adding on that the wikipedia article is surprisingly informative
for example:
Shell trick
Another way to achieve a similar effect is to use a shell as the launched command, and deal with the complexity in that shell, for example:
$ mkdir ~/backups
$ find /path -type f -name '*~' -print0 | xargs -0 bash -c 'for filename; do cp -a "$filename" ~/backups; done' bash
Inspired by an answer by #justaname above, this command which incorporates Perl one-liner will do it:
find ./ -name \*.txt | perl -p -e 's/^(.*\/(.*)\.txt)$/mv $1 .\/foo\/$2.bar.txt/' | bash

change file names using bash

Data structure is like following, and I would like to change "lane-7" to "lane-5".
I am thinking of a command like this, but it does not work.
find PATH -name "lane-7*" | xargs -i echo mv {} `echo {}|sed 's/lane-7/lane-5/'` | sh
Anyidea? Thanks
PATH/28/lane-7-22.fq
PATH/28/lane-7-21.fq
PATH/28/lane-7-18.fq
PATH/28/lane-7-24.fq
PATH/28/lane-7-23.fq
PATH/28/lane-7-19.fq
PATH/28/27/lane-7-22.fq
PATH/28/27/lane-7-21.fq
PATH/28/27/lane-7-18.fq
PATH/28/27/lane-7-24.fq
PATH/28/27/lane-7-23.fq
PATH/28/27/lane-7-19.fq
PATH/28/27/26/lane-7-22.fq
PATH/28/27/26/lane-7-21.fq
PATH/28/27/26/lane-7-18.fq
PATH/28/27/26/lane-7-24.fq
PATH/28/27/26/lane-7-23.fq
PATH/28/27/26/lane-7-19.fq
PATH/28/27/26/25/lane-7-22.fq
PATH/28/27/26/25/lane-7-21.fq
PATH/28/27/26/25/lane-7-18.fq
PATH/28/27/26/25/lane-7-24.fq
PATH/28/27/26/25/lane-7-23.fq
PATH/28/27/26/25/lane-7-19.fq
...
You could do this with a while loop and a bash string substitution:
find PATH -name "lane-7*" | while read -r file ; do
echo mv $file ${file/lane-7/lane-8}
done
Remove the echo if that appears good.
Use rename, it was made for this:
find PATH -name "lane-7*" | xargs rename "lane-7" "lane-5"
You might have the perl version of rename instead (Debian installs it by default). In that case, just use a perl expression instead:
find PATH -name "lane-7*" | xargs rename "s/^lane-7/lane-5/"

Resources