Bash: piped argument to open command fails. Open commands excutes too early? - macos

I'm pretty much a novice to shell scripting. I'm trying to send the output of some piped commands to an open command in bash in OSX.
My ultimate goal is to compile a Flex/Actionscript application from TextWrangler by calling a bash script with a little Applescript and have the result played directly in a Flash Player. The Applescript is pretty much doing it's job. But the bash script doesn't work as I expect. Same results when I ommit the Applescript and simply put it directly in terminal.
This is what the Applescript is sending to terminal:
mxmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//' | open -a 'Flash Player'
So basically, I read the last line of the output of mxmlc, which usually looks something like this:
/Users/fireeyedboy/Desktop/DocumentClass.swf (994 bytes)
and I strip everything after the first space it encounters. I know it's hardly bulletproof yet, it's still just a proof of concept. When I get this roughly working I'll refine. It returns the desired result so far:
/Users/fireeyedboy/Desktop/DocumentClass.swf
But as you can see, I then try to pipe this sed result to the Flash Player and that's where it fails. The Flash Player seems to open way too early. I would expect the Flash Player to open only after the script finished the sed command. But it opens way earlier.
So my question is twofold:
Is it even possible to pipe an
argument to the open command this
way?
Do I need to use some type
of delay command to get this
working, since the open command doesn't seem to be waiting for the input?

You're trying to give the name of the swf file as input to stdin of the open command, which it doesn't support.
It expects the file name as an argument (similar to -a).
You can do something like this:
FILENAME=`xmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//'`
open -a 'Flash Player' $FILENAME
or on a single line:
open -a 'Flash Player' `xmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//'`
If you're using bash (or another modern POSIX shell), you can replace the pretty unreadable backtick character with $( and ):
open -a 'Flash Player' $(xmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//')

All commands in a pipe are started at the same time. During this step, their input/outputs are chained together.
My guess is that open -a 'Flash Player' doesn't wait for input but simply starts the flash player. I suggest to try to run the player with an argument instead:
name=$(mxmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//')
open -a 'Flash Player' "$name"

I'm not familiar with the "open" command as it seems to be a mac thing, but i think what you want to do is:
open -a 'Flash Player' $(mxmlc -warnings=false DocumentClass.as | tail -n 1 | sed 's/[[:space:]].*$//')
In general you can't pipe arguments to a command, you have to specify that you want the output of the previous command to be treated as arguments, either as in my example or with the xargs command. Note that there is a limit on the maximum size of a command line, though.

Related

Dynamically pipe user input into a variable for ls bash

I have the following one sed liner that I use to prettify the output of lsing all my .desktop files.
ls -1 | sed -e 's/\.desktop$//' | sed -e 's/\org.gnome.//' | grep "$name" | head -1
Currently I have a read command that I pipe into that $name variable for input. Can you give me some ideas on how to make it dynamically output or autocomplete what im typing as app launchers like Rofi or others do?
You could try the peco utility. (It's also packaged for several distributions.)
From its github page:
peco can be a great tool to filter stuff like logs, process stats, find files, because unlike grep, you can type as you think and look through the current results.
The fzf "commandline fuzzy finder" utility (repos) is similar:
It's an interactive Unix filter for command-line that can be used with any list; files, command history, processes, hostnames, bookmarks, git commits, etc.
There's a list of other options at: https://alternativeto.net/software/peco/

How do I open a file in VS Code terminal by partially matching the file name?

If I have a file named w5_align_example.cpp, how do I open that file in VS Code integrated terminal by only supplying the word align?
code w5_align_sample.cpp would open it but I sometimes only remember the keyword align unless I search in a separate command to see what the file begins with. I want to open in a single command instead.
I've tried:
$ ls | grep "align" | code which gives me Run with 'code -' to read output from another program (e.g. 'echo Hello World | code -'). error.
$ ls | grep "align" | code - opens up a new file called code-stdin-sfd.txt with the text w5_align_example.cpp inside.
What would be the simplest (i.e. shortest) command to do this?
ls | grep "align" | xargs -I{} code {}
or
code $(ls | grep "align")
You can just use *. It matches any string and can be used multiple times.
code *align*
In some shells, you can combine this with tab completion. Just type:
code *align*
And then press Tab. This will fill in the rest of the file name, but it will beep if there is more than one option.

using cat in a bash script is very slow

I have very big text files(~50,000) over which i have to do some text processing. Basically run multiple grep commands.
When i run it manually it returns in an instant , but when i do the same in a bash script - it takes a lot of time. What am i doing wrong in below bash script. I pass the names of files as command line arguments to script
Example Input data :
BUSINESS^GFR^GNevil
PERSONAL^GUK^GSheila
Output that should come in a file - BUSINESS^GFR^GNevil
It starts printing out the whole file on the terminal after quite some while. How do i suppress the same?
#!/bin/bash
cat $2 | grep BUSINESS
Do NOT use cat with program that can read file itself.
It slows thing down and you lose functionality:
grep BUSINESS test | grep '^GFR|^GDE'
Or you can do like this with awk
awk '/BUSINESS/ && /^GFR|^GDE/' test

How do I get grep to keep the file/pipe open?

I am trying to debug some errors in a live Merb app. There are a lot of lined of error code running by, but I jut need to see the first one. I can use grep to select these lines and print them but it closes as soon as it reached the end of the file.
What I would like to do is use grep like the shift-F mode in less where it will keep the file open and report new matching line as they are written to the log.
- or -
Is there a way to do this directly with less that I don't know about?
try this
tail -f dev.log | grep '^ERROR:'
the -f option to tail tells it to wait for more data when it hits EOF.
Can't you do this with watch and tail?
watch -n 30 "grep 'dev.log' '^ERROR:' | tail -n 30"

How to run the first process from a list in a file deleting the first line as if the file was a queue and I called "pop"?

How to run the first process from a list of processes stored in a file and immediately delete the first line as if the file was a queue and I called "pop"?
I'd like to call the first command listed in a simple text file with \n as the separator in a pop-like fashion:
Figure 1:
cmdqueue.lst :
proc_C1
proc_C2
proc_C3
.
.
Figure 2:
Pop the first command via popcmd:
proc_A | proc_B | popcmd cmdqueue.lst | proc_D
Figure 3:
cmdqueue.lst :
proc_C2
proc_C3
proc_C4
.
.
Ooh, that's an amusing one-liner.
Okay, here's the deal. What you want is a program that, when called, prints the first line of the file to stdout, then delete that line from the file. Sounds like a job for sed(1).
Try
proc_A | proc_B | `(head -1 cmdstack.lst; sed -i -e '1d' cmdstack.lst)` | proc_D
I'm sure that someone who had already had their coffee could change the sed program to not need the head(1) call, but that works, and shows off using a subshell ("( foo )" runs in a sub-process.)
pop-cmd.py:
#!/usr/bin/env python
import os, shlex, sys
from subprocess import call
filename = sys.argv[1]
lines = open(filename).readlines()
if lines:
command = lines[0].rstrip()
open(filename, "w").writelines(lines[1:])
if command:
sys.exit(call(shlex.split(command) + sys.argv[2:]))
Example:
proc_A | proc_B | python pop-cmd.py cmdstack.lst | proc_D
I assume that you are constantly appending to the file also, so rewriting the file puts you in danger of overwriting data. For this type of task I think you would be better using individual files for each queue entry, using date/time to determine order, and then as you process each file you could append the data to a log file and then delete the trigger file.
Really need more information in order to suggest a good solution. It's important to know how the file is getting updated. Is it a lot of separate processes, just one process, etc.
I think you would need to rewrite the file - e.g. run a command to list all lines but the first, write that to a temporary file and rename it to the original. That could be done using tail or awk or perl depending on the commands you have available.
If you want to treat a file like a stack, then a better approach would be to have the top of the stack at the end of the file.
Thus you can easily cut off the file at the beginning of the last line (= pop), and simply append to the file as you push.
You can use a little bash script; name it "popcmd":
#!/bin/bash
cmd=`head -n 1 $1`
tail -n +2 $1 > ~tmp~
mv -f ~tmp~ $1
$cmd
edit: Using sed for the middle two lines, like Charlie Martin showed, is much more elegant, of course:
#!/bin/bash
cmd=`head -n 1 $1`
sed -i -e '1d' $1
$cmd
edit: You can use this exactly as in your example usage code:
proc_A | proc_B | popcmd cmdstack.lst | proc_D
You can't write to the beginning of a file, so cutting out line 1 would be a lot of work (rewrite the rest of the file (which isn't actually that much work for the programmer (it's what every other answer post has written for you :) ) ) ).
I'd recommend keeping the whole thing in memory and using a classic stack rather than a file.

Resources