how to package shell script into a leiningen project - clojure

I have a shell script saved in scripts/shell/record_video.sh that I need to call in one of my projects.
An example of its use in code:
(defn save-mp4 [cam filename duration]
(sh "scripts/shell/record_video.sh" (:i_url cam) filename (str duration)))
how would I be able to jar the project so that the shell script is included if i upload to clojars?
follow up
Thanks #Ankur. The (sh "bash" :in bash-command-string) is super useful. The only reason I needed the .sh file in the first place was because I couldn't quite figure out how to do redirects when the stdout contains something big (like a video).
the file scripts/shell/record_video.sh contains:
SRC=rtsp://$1:554/axis-media/media.amp?resolution=1920x1080
DST=$2
LEN=$3
openRTSP -4 -d $LEN -w 1440 -h 1080 -f 25 -u root pass $SRC > $DST 2> /dev/null
and I did not know how to translate the redirect (>) without making the program memory consumption enormous. The (sh "bash" :in bash-command-string) command allows my function to be written without the shell script:
(defn save-mp4 [cam filename duration]
(let [src (format "rtsp://%s:554/axis-media/media.amp?resolution=1920x1080" (:i_url cam))
cmd (format "openRTSP -4 -d %d -w 1440 -h 1080 -f 25 -u root pass %s > %s 2> /dev/null"
duration src filename)]
(sh "bash" :in cmd)))

Two steps:
Package the shell script as resource.
Read the shell script file using java resource API and use
(sh "bash" :in file-str)
Where file str is the shell script content read using resource API.

You actually have two problems. First how to put a file into a jar file. Second, how to access the file from within the jar file.
The first is simple enough: include the file on the resources directory. All files in that directory are included in the jar file.
The second is a more difficult as sh is going to be looking for the script on disk, not nestled in a jar file. You may have to extract the file using class loader.getresource and them write it to disk to execute it.
There is a discussion on how to read a resource from a jar with the simplest being (clojure.java.io/resource "myscript.js")

Related

Youtube-DL - Batchfile

I have a list of youtube urls.
The list is stored in a batch-file.txt
I would like to download each URL and rename with a given name.m4a
batch-file.txt
youtube-dl -f 'bestaudio[ext=m4a]' 'https://www.youtube.com/watch?v= ...' --output '...m4a'
youtube-dl -f 'bestaudio[ext=m4a]' 'https://www.youtube.com/watch?v= ...' --output '...m4a'
youtube-dl -f 'bestaudio[ext=m4a]' 'https://www.youtube.com/watch?v= ...' --output '...m4a'
If I run the commands individually, it works.
If I run the batch file via
youtube-dl --batch-file='batch-file.txt'
it does not work.
What do I need to write in the batch-txt file?
How do I call the batch file to download the m4a files simultaneously (if possible)
Many Thanks,
BM
Batch file contains only the URLs, no other parameters.
batch-file.txt
https://www.youtube.com/watch?v=...
https://www.youtube.com/watch?v=...
.
Here is the line to run the youtube-dl command starting with leading number 1
youtube-dl -ciw -f 'bestaudio[ext=m4a]' --batch-file='batch-file.txt' -o '%(autonumber)02d. %(title)s.%(ext)s'
Here is the line to run the youtube-dl command starting with leading number 35 (in case you want to continue at another time)
youtube-dl -ciw -f 'bestaudio[ext=m4a]' --batch-file='batch-file.txt' -o '%(autonumber)02d. %(title)s.%(ext)s' --autonumber-start 35
Missing part:
Parallel / Simultaneous Download. But I can live with the approach above.

.sh file works in terminal but not in python script (rclone w/ Raspberry Pi)

I'm having trouble running a .sh file in python. When I type in the location of the .sh file (/home/pi/file/script.sh) the script runs perfectly.
I'm trying to run this script in my python2 script and I've done the following methods:
subprocess.Popen(['bash', 'location of .sh'])
subprocess.call(['location of .sh'])
os.popen(['location of .sh'])
When I run the python script, I get a prompt from rclone saying "Command sync needs 2 arguments maximum"
My .sh file just includes:
#!/bin/sh
sudo /usr/local/bin/rclone -v sync /home/pi/some_project_data remote:rclone --delete-before --include *.csv --include *.py
I'm not sure how running the .sh file on terminal works fine, but this error pops up when I'm trying to run the .sh file using Python.
Your script fails whenever you run it in a directory containing 2 or more .csv or .py files. This is true for terminals as well as via Python.
To avoid that, quote your patterns so the shell doesn't expand them:
#!/bin/sh
sudo /usr/local/bin/rclone -v sync /home/pi/some_project_data remote:rclone \
--delete-before --include "*.csv" --include "*.py"
Please try:
os.popen('bash locationof.sh')
ex:
os.popen('bash /home/script.sh')
That worked on my machine. If you place square brackets around the string then python assumes it is a list and popen doesnt accept a list, it accepts a single string.
If the script doesnt work, then this won't fix that, but it will at least run it. If it still doesnt work, try running the script with something like
touch z.txt
and see if z.txt appears in the file explorer. If it does, then your .sh file has a problem.

Can I run my command with out lein run?

I currently run a simple cli I wrote by calling: lein run my-cli-command arg --option
How can I call my command without needing to include lein run? This is what i'm after: my-cli-command arg --option
Do I need to convert it to an binary or executable and if so how?
As far as I know, there's no way to run just my-cli-command arg --option.
You can take lein out of the equation though by creating a Java archive:
lein uberjar
Then run the jar as you would any other:
java -jar target/my-cli-command-standalone.jar arg --option
uberjar will name the jar based on what you've called your project in project.clj, and will create a jar that relies on external dependencies, and one that doesn't (standalone).
Then, as #gary pointed out, you can stick the java - jar ... command in a .bat file, name it whatever you want, then run the bat directly. My bat-Fu is pretty weak, but there's likely a way to pass arguments to the bat and have them passed to the jar so you don't need to hard code the arguments.
As of Clojure 1.9 there are new CLI tools! See this guide for installation. You can create an executable script like this:
#!/usr/local/bin/clojure
(println "Hello World! from" *ns*)
(require '[clojure.walk :as walk])
(walk/postwalk-demo {:woo ::yeah})
Then make the script executable and execute it:
$ chmod +x my_script
$ ./my_script
Hello World! from #object[clojure.lang.Namespace 0x1ebea008 user]
Walked: :woo
Walked: :user/yeah
Walked: [:woo :user/yeah]
Walked: {:woo :user/yeah}
Start-up time seems improved as well. It takes a little over a second to run a trivial script (just print a string) on a recent MBP:
time ./hello
Hello World!
./hello 1.51s user 0.12s system 184% cpu 0.887 total
I've not used it, but I know inlein exists and it looks like what you need.
Inlein is the easiest and fastest way to run Clojure scripts. You only have to inline your dependencies, add in a shebang line, and make the script file executable.
And a minimal example:
#!/usr/bin/env inlein
'{:dependencies [[org.clojure/clojure "1.8.0"]]}
(println "hello world!")
You can use "binary payload" in shell script as described in https://coderwall.com/p/ssuaxa/how-to-make-a-jar-file-linux-executable
Basically you can concatenate a shell script and your uberjar in a single shell script file and execute java in the script specifying that script as the jar file on the classpath - the example comes from the linked post:
Save your runner script in stub.sh:
#!/bin/sh
MYSELF=`which "$0" 2>/dev/null`
[ $? -gt 0 -a -f "$0" ] && MYSELF="./$0"
java=java
if test -n "$JAVA_HOME"; then
java="$JAVA_HOME/bin/java"
fi
exec "$java" $java_args -jar $MYSELF "$#"
exit 1
Then concatenate it with your uberjar:
cat stub.sh my-cli-command-uberjar.jar > my-cli-command && chmod +x my-cli-command
Now you can run it directly:
./my-cli-command args...
There is also a lein plugin automating this process: lein-binplus

How to run lein from another directory (without cd to project dir)?

Supposing my lein project is located in /some/location/project and my current location is /another/location/ how can I run lein build without changing to project location cd /some/location/project?
For example, in maven
mvn -f /path/to/pom.xml
As far as I can tell lein doesn't have an option like this. You have to be in the directory. (Perhaps someone else will correct me.) However, you could write a shell script that does what you want. For example, in a unix that provides the getopts utility, you could use the following script, which might be called "leinthere":
#!/bin/sh
if getopts f: option; then
# user supplied -f
if [ -d "$OPTARG" ]; then
# user also supplied name of a real dir, now in $OPTARG
cd "$OPTARG"
shift 2 # get rid of -f and the dir name
else
# user supplied -f, but not a real dir name
echo "usage: $0 [-f project-dir] [lein args]"
exit 1
fi
fi
# now just run lein in the normal way:
lein "$#"

What is the criteria for entering shell commands using Clojure Java Shell, specifically cd?

I have been trying a number of things to enter cd /home/ics/icsdev using Clojure Java Shell. I've been getting errors, but don't know why. I've read the source and looked for examples.
ics-db.core=> (sh "cmd" "cd /home/ics/icsdev")
IOException error=2,
No such file or directory
java.lang.UNIXProcess.forkAndExec (UNIXProcess.java:-2)
I have also played around with :in and got this:
ics-db.core=> (:in "cd /home/ics/icsdev" (sh "pwd" ))
{:exit 0, :out "/home/ics/projects/clojure/ics-db\n", :err ""}
I wanted pwd to return /home/ics/icsdev.
In contrast, pwd works just fine
ics-db.core=> (sh "pwd")
{:exit 0, :out "/home/ics/projects/clojure/ics-db\n", :err ""}
ics-db.core=>
What am I missing?
Version of lein
Leiningen 2.3.4 on Java 1.7.0_55 OpenJDK Client VM
Clojure 1.5.1
At the (bash) prompt
$ which pwd
/usr/bin/pwd
$ which cd
which: no cd in $PATH
You cannot directly exec a bash built-in like cd as a new process. It is a shell command, not an executable. This is why (sh "pwd") works but (sh "cd" ...) does not.
You can do so indirectly (Clojure REPL prompt)
=> (require '[clojure.java.shell :as shell])
=> (shell/sh "sh" "-c" "cd /etc; pwd")
{:exit 0, :out "/etc\n", :err ""}
But that just sets the directory in that new shell sub-process that has now exited.
Use :dir directive or with-sh-dir if you want to exec (multiple) process from within a different directories.
cd is a builtin in the cmd prompt. So, you can't execute it in this way, and even if you could, it wouldn't matter because it would change the current working directory of the sub-process, and not the JVM that is spawning the subprocess.