This is my code, it runs but does nothing.
ls | ruby -ne 'File.delete($_) if File.extname($_)==".jpg"'
I thought it was string comparisons issue but the below code works.
ruby -e 'puts "dsad" if "e" == "e"'
This leads me to believe $_ is a different Type. How do I run comparison on it, specifically as it relates to my initial code line.
Remove the newlines.
ls | ruby -ne 'File.delete($_.chomp) if File.extname($_.chomp)==".jpg"'
Related
I understand the following:
$ ruby -e "p 'abc'.sub('a','A').sub('b', 'B')"
"ABc"
I am OK with the following also:
echo abc | ruby -p -e "sub('a','A');sub('b', 'B')"
ABc
But:
echo abc | ruby -p -e "sub('a','A').sub('b', 'B')"
Abc
I expect the result to be "ABc" as well, why is it not? The second sub('b', 'B') is not operational.
The two cases look similar, but in fact you are running different methods from the Ruby core library in them:
In your first case, i.e. sub('a','A');sub('b', 'B'):
You are running both sub without specifying an explicit receiver, and therefore you are invoking the method Kernel#sub. The Ruby-Doc says about this method:
sub(pattern, replacement) → $_
Equivalent to $_.sub(args), except that $_ will be updated if substitution occurs. Available only when -p/-n command line option specified.
The following examples illustrate the differences with and without a receiver:
$ echo abc | ruby -p -e '$_.sub("a","A").sub("b", "B"); $_'
abc
$ echo abc | ruby -p -e 'sub("a","A").sub("b", "B"); $_'
Abc
Hence, in the first example, you really invoke that Kernel#sub twice, and after each invocation, $_ is updated. Therefore, $_ is ABc after the second sub has been executed. At the end of the of the whole expression supplied by -e (i.e. at the end of the implicit loop provided by the -p option), the value of $_ is printed, and you see ABc.
In your second example, i.e.
sub('a','A').sub('b', 'B')
The first sub again is Kernel#sub, as before. It has the effect of turning the string into Abc, and also sets $_ to Abc. However, the second sub now does have an explicit receiver (the string resulting from the first sub), and in this case, the method String#sub is executed. This method produces ABc, but different to Kernel#sub, it does not update $_. Therefore, $_ is still set to Abc, and this is what you see as output.
While it seems to be convenient to rely on the implicit effect certain methods have on $_, it is sometimes easier to make the manipulation explicit. For instance, if you do a
$_=$_.sub('a','A').sub('b','B')
you can clearly see what is going on. An alternative would be
$_.sub!('a','A');$_.sub!('b','B')
Note that in the last case, you do not want to chain the two sub!, because String#sub! returns nil if no substitutions have been performed.
I've answered my own question in writing this, but it might be helpful for others as I couldn't find a straightforward answer anywhere else. Please delete if inappropriate.
I'm trying to construct an if statement depending whether some <STRING> is found inside the environment $PATH.
When I pipe $PATH through grep I get a successful hit:
echo $PATH | grep -i "<STRING>"
But I was really struggling to find the syntax required to construct an if statement around this. It appears that the line below works. I know that the $(...) essentially passes the internal commands to the if statement, but I'm not sure why the [[...]] double brackets are needed:
if [[ $(echo $PATH | grep -i "<STRING>") ]]; then echo "HEY"; fi
Maybe someone could explain that for me to have a better understanding.
Thanks.
You could make better use of shell syntax. Something like this:
$ STRING="bin"
$ grep -i $STRING <<< $PATH && echo "HEY"
That is: first, save the search string in a variable (which I called STRING so it's easy to remember), and use that as the search pattern. Then, use the <<< redirection to input a "here string" - namely, the PATH variable.
Or, if you don't want a variable for the string:
$ grep -i "bin" <<< $PATH && echo "HEY"
Then, the construct && <some other command> means: IF the exit status of grep is 0 (meaning at least one successful match), THEN execute the "other command" (otherwise do nothing - just exit as soon as grep completes). This is the more common, more natural form of an "if... then..." statement, exactly of the kind you were trying to write.
Question for you though. Why the -i flag? That means "case independent matching". But in Unix and Linux file names, command names, etc. are case sensitive. Why do you care if the PATH matches the string BIN? It will, because bin is somewhere on the path, but if you then search for the BIN directory you won't find one. (The more interesting question is - how to match complete words only, so that for example to match bin, a directory name bin should be present; sbin shouldn't match bin. But that's about writing regular expressions - not quite what you were asking about.)
The following version - which doesn't even use grep - is based on the same idea, but it won't do case insensitive matching:
$ [[ $PATH == *$STRING* ]] && echo "HEY"
[[ ... ]] evaluates a Boolean expression (here, an equality using the * wildcard on the right-hand side); if true, && causes the execution of the echo command.
you don't need to use [[ ]], just:
if echo $PATH | grep -qi "<STRING>"; then echo "HEY"; fi
With perl, you can do this:
$ perl -pi -e 's/foo/bar/g' *.txt
Which will replace the string "foo" with "bar" on all *.txt files in the current directory.
I like this, but I was wondering if the same thing is possible using Ruby.
Yep. Ruby has an equivalent for most of Perl's command line options, and many of them are identical.
$ ruby -pi -e 'gsub /foo/, "bar"' *.txt
Here are the relevant docs from man ruby:
-i extension – Specifies in-place-edit mode. The extension,
if specified, is added to old file name to make a backup copy. For
example:
% echo matz > /tmp/junk
% cat /tmp/junk
matz
% ruby -p -i.bak -e '$_.upcase!' /tmp/junk
% cat /tmp/junk
MATZ
% cat /tmp/junk.bak
matz
-n – Causes Ruby to assume the following loop around your
script, which makes it iterate over file name arguments somewhat like
sed -n or awk.
while gets
...
end
-p – Acts mostly same as -n switch, but print the value of
variable $_ at the each end of the loop. For example:
% echo matz | ruby -p -e '$_.tr! "a-z", "A-Z"'
MATZ
My code above uses Kernel#gsub, which is only available in -p/-n mode. Per the docs:
gsub(pattern, replacement) → $_
gsub(pattern) {|...| block } → $_
Equivalent to $_.gsub..., except that $_ will be updated if
substitution occurs. Available only when -p/-n command line option
specified.
There are a handful of other such Kernel methods, which are useful to know: chomp, chop, and (naturally) sub.
Check out man ruby; there are a lot of great features.
I need to see if user exists in /etc/passwd. I'm using grep, but I'm having a hard time passing multiple patterns to grep.
I tried
if [[ ! $( cat /etc/passwd | egrep "$name&/home" ) ]];then
#user doesn't exist, do something
fi
I used ampersand instead of | because both conditions must be true, but it's not working.
Try doing this :
$ getent passwd foo bar base
Finally :
if getent &>/dev/null passwd user_X; then
do_something...
else
do_something_else...
fi
Contrary to your assumptions, regex does not recognize & for intersection, even though it would be a logical extension.
To locate lines which match multiple patterns, try
grep -e 'pattern1.*pattern2' -e 'pattern2.*pattern1' file
to match the patterns in any order, or switch to e.g. Awk:
awk '/pattern1/ && /pattern2/' file
(though in your specific example, just "$name.*/home" ought to suffice because the matches must always occur in this order).
As an aside, your contorted if condition can be refactored to just
if grep -q pattern file; then ...
The if conditional takes as its argument a command, runs it, and examines its exit code. Any properly written Unix command is written to this specification, and returns zero on success, a nonzero exit code otherwise. (Notice also the absence of a useless cat -- almost all commands accept a file name argument, and those which don't can be handled with redirection.)
I'm trying to create a very simple bash script that will open new link base on the input command
Use case #1
$ ./myscript longname55445
It should take the number 55445 and then assign that to a variable which will later be use to open new link based on the given number.
Use case #2
$ ./myscript l55445
It should do the exact same thing as above by taking the number and then open the same link.
Use case #3
$ ./myscript 55445
If no prefix given then we just simply open that same link as a fallback.
So far this is what I have
#!/bin/sh
BASE_URL=http://api.domain.com
input=$1
command=${input:0:1}
if [ "$command" == "longname" ]; then
number=${input:1:${#input}}
url="$BASE_URL?id="$number
open $url
elseif [ "$command" == "l" ]; then
number=${input:1:${#input}}
url="$BASE_URL?id="$number
open $url
else
number=${input:1:${#input}}
url="$BASE_URL?id="$number
open $url
fi
But this will always fallback to the elseif there.
I'm using zsh at the moment.
input=$1
command=${input:0:1}
sets command to the first character of the first argument. It's not possible for a one character string to be equal to an eight-character string ("longname"), so the if condition must always fail.
Furthermore, both your elseif and your else clauses set
number=${input:1:${#input}}
Which you could have written more simply as
number=${input:1}
But in both cases, you're dropping the first character of input. Presumably in the else case, you wanted the entire first argument.
see whether this construct is helpful for your purpose:
#!/bin/bash
name="longname55445"
echo "${name##*[A-Za-z]}"
this assumes a letter adjacent to number.
The following is NOT another way to write the same, because it is wrong.
Please see comments below by mklement0, who noticed this. Mea culpa.
echo "${name##*[:letter:]}"
You have command=${input:0:1}
It takes the first single char, and you compare it to "longname", of course it will fail, and go to elseif.
The key problem is to check if the input is beginning with l or longnameor nothing. If in one of the 3 cases, take the trailing numbers.
One grep line could do it, you can just grep on input and get the returned text:
kent$ grep -Po '(?<=longname|l|^)\d+' <<<"l234"
234
kent$ grep -Po '(?<=longname|l|^)\d+' <<<"longname234"
234
kent$ grep -Po '(?<=longname|l|^)\d+' <<<"234"
234
kent$ grep -Po '(?<=longname|l|^)\d+' <<<"foobar234"
<we got nothing>
You can use regex matching in bash.
[[ $1 =~ [0-9]+ ]] && number=$BASH_REMATCH
You can also use regex matching in zsh.
[[ $1 =~ [0-9]+ ]] && number=$MATCH
Based on the OP's following clarification in a comment,
I'm only looking for the numbers [...] given in the input.
the solution can be simplified as follows:
#!/bin/bash
BASE_URL='http://api.domain.com'
# Strip all non-digits from the 1st argument to get the desired number.
number=$(tr -dC '[:digit:]' <<<"$1")
open "$BASE_URL?id=$number"
Note the use of a bash shebang, given the use of 'bashism' <<< (which could easily be restated in a POSIX-compliant manner).
Similarly, the OP's original code should use a bash shebang, too, due to use of non-POSIX substring extraction syntax.
However, judging by the use of open to open a URL, the OP appears to be on OSX, where sh is essentially bash (though invocation as sh does change behavior), so it'll still work there. Generally, though, it's safer to be explicit about the required shell.