Difference between \`nonexistent_command\` and \``"nonexistent_command"\` - ruby

Why do \`some nonexistent command\` and `\`"some nonexistent command"\` behave differently?
irb> \`nonexistent_command\`
Errno::ENOENT: No such file or directory - nonexistent_command
in ``'
irb> \`"nonexistent_command"\`
sh: nonexistent_command: command not found
=> ""
Why is it like this?

when you use backticks ` that is telling ruby to go out and try and run that system command. You are getting an "Error no entry" (ENOENT) error back from the operating system which can't find that command or file.
when you use double quotes " you are creating a string and that is no problem.
food for thought: perhaps you mean to use single quotes (which is simply a string that does not get variable interpolation done on it) instead of backticks?

Related

How to grab value back from external script in bash?

I'm sure I'm missing something stupid. I want to pass a full path variable to a perl script, where I do some work on it and then pass it back. So I have:
echo "Backing up: $f ";
$write_file="$(perl /home/spider/web/foo.com/public_html/gen-path.cgi $f)";
echo "WRITE TO: $write_file \n";
However, this gives me:
Backing up: /home/spider/web/foo.com/public_html/websites-uk/uk/q/u
backup-files-all.sh: line 7: =backup-uk-q-u.tar.gz: command not found
WRITE TO: \n
I can't work out why its not saving the output into $write_file. I must be missing something (bash isn't my prefered language, which is why I'm passing to Perl as I'm a lot more fluent in that :))
Unless your variable write_file already exists, the command $write_file="something" will translate to ="something"(1).
When setting a variable, leave off the $ - you only need it if you want the value of the variable.
In other words, what you need is (note no semicolons needed):
write_file="$(perl /home/spider/web/foo.com/public_html/gen-path.cgi $f)"
(1) It can be even hairier if it is set to something. For example, the code:
write_file=xyzzy
$write_file="something"
will result in something being placed into a variable called xyzzy, not write_file :-)

Looking for an explanation of how to use PNGlitch

I have installed the Ruby environment manager rbenv, Ruby, RubyGems, and PNGlitch (on macOS). Now how do I use PNGlitch?
The best documentation I have been able to find is on this page, and here's an example of the syntax given:
How to use this library: The Simple Way
png = PNGlitch.open '/path/to/your/image.png'
png.glitch do |data|
data.gsub /\d/, 'x'
end
png.save '/path/to/broken/image.png'
png.close
Okay, great. When I insert my file paths, save that code as an .rb file, and open it, I just get:
test.rb: command not found
If I paste it directly into Terminal I get:
png = PNGlitch.open '/Users/username/Documents/testimage.png'
-bash: png: command not found
ComputerName:~ username$ png.glitch do |data|
> data.gsub /\d/, 'x'
-bash: png.glitch: command not found
-bash: data: command not found
-bash: data.gsub: command not found
ComputerName:~ username$ end
-bash: end: command not found
ComputerName:~ username$ png.save '/Users/username/Documents/testimage_glitched.png'
-bash: png.save: command not found
ComputerName:~ username$ png.close
I also tried the syntax given on this page and entered:
pnglitch /Users/username/Documents/testimage.png –filter=Sub /Users/username/Documents/testimage_glitched.png
...this resulted in getting the following message:
tried to create Proc object without a block
Usage:
pnglitch <infile> [--filter=<n>] <outfile>
Options:
-f, --filter=<n> Fix all filter types as passed value before glitching.
A number (0..4) or a type name (none|sub|up|average|paeth).
--version Show version.
-h, --help Show this screen.
↑ I guess this is the developer's idea of documentation. 🤣
Well, trying to follow that example I also did this:
pnglitch </Users/username/Documents/testimage.png> [--filter=<2>] </Users/username/Documents/testimage_glitched.png>
...but that only resulted in:
-bash: syntax error near unexpected token 2
(I chose 2 because apparently that corresponds to the "Sub" filter.)
I tried variants of this syntax as well, including omitting characters <> and [].
There must be some assumed knowledge here that I don't have. So what I would like to know is:
How can I actually use PNGlitch to glitch a PNG image?
How can I use PNGlitch to glitch all the PNG images in a folder?
Any additional advice on using different filters would also be appreciated.
Thank you.
There's a lot going on here that needs to be cleared up.
Ruby scripts need Ruby to run
You can't just paste these into bash and expect anything useful to happen.
The usual procedure is one of two variants. Either:
Create a .rb script, like example.rb
Run ruby example.rb where that's your script name at the end.
Or use the "hash-bang" method:
Create a script with #!/usr/bin/env ruby as the very first line.
Make this script executable with chmod +x example.rb
Run this script directly, ./example.rb or whatever path it has.
Note that example.rb by itself will not work unless it is in your path, hence the ./ is necessary.
Command line example syntax
Here <name> has special meaning, where it's just a way of saying name as if it had italics or special formatting. On a text-mode terminal it's not practical to add syntax like that, it's limited to ASCII in most cases, so this tradition evolved.
Within the POSIX shell > and < have special meaning, they're used to, respectively, redirect input to or from a file. For example, ls > ls.txt dumps the output of ls into a file called ls.txt, while cat < ls.txt reads in the contents of ls.txt and displays it.
Things like [name] mean optional arguments, like [--help] means the --help argument is optional.
Within the POSIX shell [ and ] have special meaning. They can be used in an if construct, but more commonly in file wildcards, like l[abc].txt means any of la.txt, lb.txt or lc.txt.
Putting this together it's possible to understand the notation used here:
pnglitch <infile> [--filter=<n>] <outfile>
Where that means infile is your "input file" argument, and outfile is your "output file" argument, and --filter is an optional argument taking n as an input.
So you call it like this:
pnglitch input.png output.png
Or with an option, like you did:
pnglitch testimage.png --filter=sub testimage_glitched.png
Though note I've used lower-case sub as that's precisely what's in the help output and following casing conventions usually matters.

While using a single quote in a string in shell with Ruby I get sh: -c: line 0: unexpected EOF while looking for matching `''

This works fine in Ruby, and it should work:
puts 'don\'t'
But I want to run the same in BASH with Ruby:
%x(echo 'don\'t')
I get this error:
sh: -c: line 0: unexpected EOF while looking for matching''`
Same error occurs with ``, system(), Open3
My actual code snippet:
require 'open3'
module XdoTool
BIN = 'xdotool'
EXEC = ::ENV['PATH'].split(::File::PATH_SEPARATOR).map { |path| ::File.join(path, BIN) if ::File.executable?(::File.join(path, BIN)) }.compact.last
raise RuntimeError, "No #{BIN} found in the exported paths. Please make sure you have #{BIN} installed" unless EXEC
class << self
def type(str)
Open3.capture2("#{EXEC} type --delay 0 '#{str = str.gsub("'", '\'')}'")
str
end
end
end
# Types the quoted text anywhere.
XdoTool.type("What is the reason of the error?")
# sh: -c: line 0: unexpected EOF while looking for matching `''
# sh: -c: line 1: syntax error: unexpected end of file
XdoTool.type("What's the reason of the error?")
Please do note that the str can have anything. It can contain alphanumeric characters, symbols, emojis, or combination of all these things. How can I get around the problem with quotes here?
In shell, you simply cannot include a single quote inside a single-quoted string. It has to be in a double-quoted string. That means, if you want an argument that contains both, you need to concatenate separately quoted stings together.
echo 'He said "I can'"'"t'"'
or escape the double quotes inside a double-quoted string
echo "He said \"I can't\""
(Some shells provide yet another form of quoting that can contain an escaped single quote, namely $'He said "I can\'t"'. However, that's an extension to the POSIX standard that you can't assume is supported by the
shell Ruby will use to execute your command.)

ruby: method_missing backtick typo?

I was building a method to send me an email through mutt when my ruby scripts fail. It looks something like this:
begin
UnknownFunction()
rescue
subject = 'Error'
to_array = ['email#email.com','email2#email.com']
body = "An error occurred:\n#{$!}"
%x[echo "#{body}" | mutt -s "#{subject}" #{to_array.join(",")}]
end
the command was throwing the following error:
sh: -c: line 1: unexpected EOF while looking for matching ``'
sh: -c: line 2: syntax error: unexpected end of file
I finally looked closely enough to see that $! contains a backtick before the undefined method's name followed by a single quote:
undefined method `UnknownFunction' for main:Object
I dug into the code and verified the method_missing method has a backtick before and single quote afterwards. Should the backtick be a single quote or vice versa? If not, what's the reasoning behind it?
raise NoMethodError, "undefined method `#{mid}' for #{self}", caller(1)
It's a substitute for an open single quote (‘) in plain text/pre-Unicode environments. See: Why do plain-text technical articles often enclose terms within backticks and single quotes?
The description of the NoMethodError is not meant to be code, so the use of a backtick there is purely for aesthetic reasons. If you want to pass an arbitrary string to the shell, use Shellwords.shellescape.
backticks are fine for simple commands, but once you start throwing data at the child process I think its better to use something more sophisticated than piping the text via echo. I'd use IO.popen:
IO.popen(
"mutt -s '%s' %s" % [ subject, to_array.join(',') ],
'w'
) do |mutt|
mutt.puts body
end
That's untested but is what I'd start with. It's more readable because it gets rid of the backtick jungle of interpolated variables. It also avoids potential problems of the sub-shell trying to help by interpreting variables or looking for embedded backticks in the text being sent to mutt.

Bash variable character replacement ends up to an empty string or a command not valid

I am working on a shell script to retrieve variable content from a JSON file via JQ. The JSON file is in string format (no matter whether this is a real string or a number) and to retrieve the variable in my bash script I did something like this
my_domain=$(cat /vagrant/data_bags/config.json | jq ."app"[0]."domain")
The above code once echoed results in "mydomain" with a beginning and a trailing quote sign. I though this was a normal behaviour of the echo command. However, while concatenating my variable with another shell command the system raise an error. For instance, the following command
cp /vagrant/public_html/index.php "/var/www/"+$my_domain+"/index.php"
fails with the following error
cp: cannot create regular file `/var/www/+"mydomain"+/index.php': No such file or directory
At this stage, I wasn't able to identify whether it's me doing the wrong concatenation with the plus sign or the variable is effectively including the quotes that in any case will end up generating an error.
I have tried to replace the quotes in my variable, but I ended up getting the system raising a "Command not found" error.
Can somebody suggest what am I doing wrong?
+ is not used for string concatenation in bash (or perl, or php). Just:
cp /vagrant/public_html/index.php "/var/www/$my_domain/index.php"
Embedding a variable inside a double-quoted text string is known as interpolation, and is one of the reasons why we need the $ prefix, to indicate that this is a variable. Interpolation is specifically not done inside single quoted strings.
Braces ${my_domain} are not required because the / directory separators are not valid characters in a variable name, so there is no ambiguity.
For example:
var='thing'
echo "Give me your ${var}s" # Correct, appends an 's' after 'thing'
echo "Give me your $vars" # incorrect, looks for a variable called vars.
If a variable (like 'vars') does not exist then (by default) it will not complain, it will just give an empty string. Braces (graph brackets) are required more in c-shell (csh or tcsh) because of additional syntax for modifying variables, which involves special trailing characters.
You don't need to use + to concatenate string in bash, change your command to
cp /vagrant/public_html/index.php "/var/www/"${my_domain}"/index.php"
My problem was not related only to the wrong concatenation, but also to the JQ library that after parsing the value from the JSon file was returning text between quotes.
In order to avoid JQ doing this, just add the -rawoutput parameter when calling JQ.

Resources