Mac Automater: from a string, get a file - service

I'm trying to make a shortcut via an automater service that will move the selected file(s) up a directory. It goes as follows:
Get Selected Finder Items
Get Value of Variable Path
Run Applescript:
on join(someList, delimiter)
set prevTIDs to AppleScript's text item delimiters
set AppleScript's text item delimiters to delimiter
set output to "" & someList
set AppleScript's text item delimiters to prevTIDs
return output
end join
to split(someText, delimiter)
set AppleScript's text item delimiters to delimiter
set someText to someText's text items
set AppleScript's text item delimiters to {""}
return someText
end split
on run {input, parameters}
set pathToMe to POSIX path of (item 1 of input as text)
set newPath to split(pathToMe, "/")
set revPath to reverse of newPath
set restList to rest of revPath
set restList to rest of restList
set joinPath to join(reverse of restList, "/")
set source to POSIX file joinPath
return source
end run
Set Value of Variable Parent
Move Finder Items To Parent
The Applescript parses the first file path in the Path in order to find the item's grandparent, returning it as a POSIX file string. The problem is that the "Move Finder" action only accepts Files/Folders. How can I select the target parent folder with the resulting string in order to pass it to the "Move Finder" action?
Things I've tried:
Using mv in a Run Bash Script: the Run Applescript action doesn't seem to return anything to the Run Bash Script; set to input as arguments, "$#" is always empty.
Doing a tell finder in Run Applescript. No error or warning, just nothing happens.
Manually setting the value of the parent variable.
Thanks in advance!

return a path of type alias in a list instead of a posix file
on run {input, parameters}
set pathToMe to (item 1 of input) as text
set f to my getParent(pathToMe, ":")
return {f as alias}
end run
to getParent(someText, delimiter)
set prevTIDs to AppleScript's text item delimiters
set AppleScript's text item delimiters to delimiter
set n to -2
if someText ends with ":" then set n to -3
set t to text 1 thru text item n of someText
set AppleScript's text item delimiters to prevTIDs
return t
end getParent

I prefer to do this all in applescript so try this code. I didn't test it but it should work. You can still add this to automator with an applescript action but you don't need all the other actions. It will do everything itself. Good luck.
tell application "Finder"
set theSelection to get selection
set parentFolder to container of (item 1 of theSelection)
move theSelection to parentFolder
end tell

Related

Powershell or Batch to execute a command with user-input parameters and to save them in a text file for future defaults

I have a program I want to execute with a line like this:
gams.exe program.gms user-defined-variable1 user-defined-variable2 ..........
Currently I use a batch file which prompts the user for input strings which are the user-defined variables, for example:
code:
set /P scen_num="(What scenario number would you like to simulate?) "
output:
(What scenario number would you like to simulate?)
the user types: 1
I use this batch file very often and there are multiple parameters. I would like to write the most-recent parameters to a text file, so that the next time I run the batch (or Powershell) file, the previously-used setting will be there on the prompt:
(What scenario number would you like to simulate?) 1
So I can spam ENTER if I want to execute exactly the same thing as the previous time.
Ideally I want to be able to do this in batch, though Powershell is acceptable if batch cannot do this.
How do I output to / read from a text file?
How do I make the prompt already contain a specified value before I type anything?
Another solution would be making a config batch, to save variables and call to set. You could just add something like this after all the variables are set.
Program.bat
:[ First variable acts to wipe file with single `>` ]
echo set var1=%var1% > config.bat
echo set var2=%var2% >> config.bat
echo set var3=%var3% >> config.bat
:[ etc ]
This will create a batch like so:
config.bat
:[ setting variables to random data for examples sake.
set var1=Hello
set var2=I am
set var3=text.
You can add the option to either manually set your variables, or just
call config.bat
And if you wish to have multiple default settings, you can just add these as seperate batch scripts, by adjusting Program.bat to something like:
Program2.bat
echo set var1=%var1% > %name%.bat
echo set var2=%var2% >> %name%.bat
echo set var3=%var3% >> %name%.bat
Setting %name% to whatever you wish if you opt to save your settings into a batch, and then just replace
call config.bat
:[ with ]
call %name%.bat
You did mention the ability to just tap enter for a variable you wanted to set default, that could be done with something like:
:[ Have the variables saved in config.bat set to retrievable variables ]
call config.bat
set 1=%var1%
set 2=%var2%
set 3=%var3%
:[ Set the variables, if `enter` pressed set to retrievable variable ]
echo Enter var1
set /p var1=
if not defined var1 (
set var1=%1%
)
echo Enter var2
set /p var2=
if not defined var2 (
set var2=%2%
)
echo Enter var3
set /p var3=
if not defined var3 (
set var3=%3%
)

substitution within a text file, using Applescript and sed

The question is a sequel to plain text URL to HTML code (Automator/AppleScript).
Suppose I have a plain txt file /Users/myname/Desktop/URLlist.txt:
title 1
http://a.b/c
title 2
http://d.e/f
...
I'd like to (1) convert all the URL (http://...) to HTML code, and (2) add
<br />
to each empty line, so that the aforementioned content will become:
title 1
http://a.b/c
<br />
title 2
http://d.e/f
<br />
...
I come to the following Applescript:
set inFile to "/Users/myname/Desktop/URLlist.txt"
set middleFile to "/Users/myname/Desktop/URLlist2.txt"
set outFile to "/Users/myname/Desktop/URLlist3.txt"
do shell script "sed 's/\\(http[^ ]*\\)/<a href=\"\\1\">\\1<\\/a>/g' " & quoted form of inFile & " >" & quoted form of middleFile
do shell script "sed 's/^$/\\ <br \\/>/g' " & quoted form of middleFile & " >" & quoted form of outFile
It works, but it is redundant (and silly?). Could anyone make it more succinct? Can it be done involving only one text file instead of three (i.e. the original content in /Users/myname/Desktop/URLlist.txt is overwritten with the end result)?
Thank you very much in advance.
Try:
set inFile to "/Users/myname/Desktop/URLlist.txt"
set myData to (do shell script "sed '
/\\(http[^ ]*\\)/ a\\
<br />
' " & quoted form of inFile & " | sed 's/\\(http[^ ]*\\)/<a href=\"\\1\">\\1<\\/a>/g' ")
do shell script "echo " & quoted form of myData & " > " & quoted form of inFile
This will let you use the myData variable later in your script. If this is not part of a larger script and you are simply modifying your file, use the -i option as jackjr300 suggests. Also, this script looks for the original pattern and appends the new line to it rather than simply looking for empty lines.
EDIT:
set inFile to "/Users/myname/Desktop/URLlist.txt"
set myData to (do shell script "sed 's/\\(http[^ ]*\\)/<a href=\"\\1\">\\1<\\/a>/g; s/^$/\\ <br \\/>/g' " & quoted form of inFile)
do shell script "echo " & quoted form of myData & " > " & quoted form of inFile
Use the -i '' option to edit files in-place.
set inFile to "/Users/myname/Desktop/URLlist.txt"
do shell script "sed -i '' 's:^$:\\ <br />:; s:\\(http[^ ]*\\):\\1:g' " & quoted form of inFile
If you want a copy of the original file, use a specified extension like sed -i ' copy'
--
Updated:
A `DOCTYPE is a required preamble.
DOCTYPEs are required for legacy reasons. When omitted, browsers tend to use a different rendering mode that is incompatible with some specifications. Including the DOCTYPE in a document ensures that the browser makes a best-effort attempt at following the relevant specifications.
The HTML lang attribute can be used to declare the language of a Web page or a portion of a Web page. This is meant to assist search engines and browsers. According to the W3C recommendation you should declare the primary language for each Web page with the lang attribute inside the <html> tag
The <meta> tag provides metadata about the HTML document. <meta> tags always goes inside the <head> element.
The http-equiv attribute provides an HTTP header for the information/value of the content attribute.
content: the value associated with the http-equiv or name attribute.
charset: To display an HTML page correctly, the browser must know what character-set to use.
In this script: I put "utf-8" as encoding, change it by the encoding of your original file.
set inFile to "/Users/myname/Desktop/URLlist.html" -- text file with a ".html" extension
set nL to linefeed
set prepandHTML to "<!DOCTYPE html>\\" & nL & "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en-US\" lang=\"en-US\">\\" & nL & tab & "<head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\\" & nL & "</head>\\" & nL
do shell script "sed -i '' 's:^$:\\ <br />:; s:\\(http[^ ]*\\):\\1:g; 1s~^~" & prepandHTML & "~' " & quoted form of inFile
do shell script "echo '</html>' " & quoted form of inFile -- write last HTML tag
I can't understand sed commands very well (it makes my brain hurt) so here's the applescript way to do this task. Hope it helps.
set f to (path to desktop as text) & "URLlist.txt"
set emptyLine to " <br />"
set htmlLine1 to "<a href=\""
set htmlLine2 to "\">"
set htmlLine3 to "</a>"
-- read the file into a list
set fileList to paragraphs of (read file f)
-- modify the file as required into a new list
set newList to {}
repeat with i from 1 to count of fileList
set thisItem to item i of fileList
if thisItem is "" then
set end of newList to emptyLine
else if thisItem starts with "http" then
set end of newList to htmlLine1 & thisItem & htmlLine2 & thisItem & htmlLine3
else
set end of newList to thisItem
end if
end repeat
-- make the new list into a string
set text item delimiters to return
set newFile to newList as text
set text item delimiters to ""
-- write the new string back to the file overwriting its contents
set openFile to open for access file f with write permission
write newFile to openFile starting at 0 as text
close access openFile
EDIT: if you have trouble with the encoding these 2 handlers will handle the read/write properly. So just insert them in the code and adjust those lines to use the handlers. Good luck.
NOTE: when opening the file using TextEdit, use the File menu and open specifically as UTF-8.
on writeTo_UTF8(targetFile, theText, appendText)
try
set targetFile to targetFile as text
set openFile to open for access file targetFile with write permission
if appendText is false then
set eof of openFile to 0
write «data rdatEFBBBF» to openFile starting at eof -- UTF-8 BOM
else
tell application "Finder" to set fileExists to exists file targetFile
if fileExists is false then
set eof of openFile to 0
write «data rdatEFBBBF» to openFile starting at eof -- UTF-8 BOM
end if
end if
write theText as «class utf8» to openFile starting at eof
close access openFile
return true
on error theError
try
close access file targetFile
end try
return theError
end try
end writeTo_UTF8
on readFrom_UTF8(targetFile)
try
set targetFile to targetFile as text
targetFile as alias -- if file doesn't exist then you get an error
set openFile to open for access file targetFile
set theText to read openFile as «class utf8»
close access openFile
return theText
on error
try
close access file targetFile
end try
return false
end try
end readFrom_UTF8

Displaying List of AutoHotkey Hotkeys

I’ve written script that contains numerous hotkeys (general structure is as below). I would like to create another one that when pressed displays a list of all of the hotkeys and their corresponding descriptions that the script contains in a nice, formatted table.
The formatting and display are tenuous since AutoHotkey’s output is limited to message-boxes, but possible. More problematic is getting the hotkeys and corresponding descriptions.
The hotkeys all call the same function with different arguments. I considered adding a variable to the function so that depending on the value, the function either performs the normal function when triggered by the normal hotkeys, or builds a string or something when triggered from the special display hotkey.
I cannot figure out a way to programmatically access the script’s hotkeys at all. I checked the docs and there don’t seem to be any A_ variables that can be used for this purpose, nor does the Hotkey command lend itself well (it can be used to test if a hotkey exists, but looping through the innumerable combinations is, at best, tedious).
Failed attempts:
I tried using Elliot’s suggestion of parsing the script itself (replacing the path with %A_ScriptFullPath%, and while it does work for a raw script, it does not when the script is compiled
I tried assigning the entire hotkey section of the script to a variable as a continuation section and then parsing the variable and creating hotkeys using the Hotkey command. This worked well right up until the last part because the Hotkey command cannot take arbitrary commands as the destination and requires existing labels.
The ListHotkeys command is not applicable because it only displays the hotkeys as plain text in the control window.
Does anyone know how I can display a list of the hotkeys and either their corresponding arguments or comments?
Example script:
SomeFunc(foobar)
{
MsgBox %foobar%
}
!^#A::SomeFunc("a") ; blah
^+NumpadMult::SomeFunc("c") ; blivet
^+!#`::SomeFunc("b") ; baz
^#Space::SomeFunc("d") ; ermahgerd
…
Example desired “outputs”:
C+A+ W+ A a | C+ S+ NumpadMult b
------------------+----------------------
C+A+S+W+ ` c | C+ W+ Space d
    or
Ctrl Alt Shift Win Key Action
-----------------------------------------
× × × A blah
× × NumpadMult baz
× × × × ` blivet
× × Space ermahgerd
etc.
The only thing I can think of is to read each line of your script individually and parse it. This code reads your script (script.ahk) one line at a time and parses it. This should get you started. Additionally, you could parse the line to check for the modifiers as well.
Loop
{
FileReadLine, line, C:\script.ahk, %A_Index%
if ErrorLevel
break
If Instr(line, "::")
{
StringSplit, linearray, line, ::,
key := linearray1
StringSplit, commandarray, linearray3, `;
action := commandarray2
hotkeyline := "key: " . key . "`tAction: " . action
final .= hotkeyline . "`r"
}
}
msgbox % final
return
I found a solution. It is not perfect (or ideal), and hopefully a proper, built-in method will become available in the future, but it works well (enough) and for raw and compiled scripts.
What I did was to use the FileInstall command which tells the compiler to add a file to the executable (and extract it when run).
Sadly, the FileInstall command will not allow the use of variables for the source file, so I cannot simply include the script itself (FileInstall, %A_ScriptFullPath%, %A_Temp%\%A_ScriptName%, 1).
As a work-around, I ended up extracting all of the desired hotkeys to a second file which I then parse as Elliot suggested, then delete, and #Include at the end of my script (it must be at the end since hotkeys will terminate the autoexecute section).
;;;;; Test.ahk ;;;;;
; Add hotkey file to executable and extract to Temp directory at runtime
FileInstall, Hotkeys.ahk, %A_Temp%\Hotkeys.ahk, 1
Loop
{
;Read a line from the extracted hotkey script and quit if error
FileReadLine, line, %A_Temp%\Hotkeys.ahk, %A_Index%
if ErrorLevel
break
;Trim whitespace
line=%line%
; Parse the line as in Elliot’s answer, but with tweaks as necessary
ParseHotkey(line)
…
}
FileDelete, %A_Temp%\Hotkeys.ahk ; Delete the extracted script
DisplayHotkeys() ; I ended up bulding and using a GUI instead
#Include, Hotkeys.ahk ; It is included at compile-time, so no A_Temp
;;;;; Hotkeys.ahk ;;;;;
z::MsgBox foo
y::MsgBox bar

Unicode named Folder shows ? in wscript prompt

I am facing problems with Unicode named folders. When I drag the folder to the script, it doesn't show the path of the folder properly.
Simple VBScript (this is just a portion of it):
Dim Wshso : Set Wshso = WScript.CreateObject("WScript.Shell")
Dim FSO : Set FSO = CreateObject("Scripting.FileSystemObject")
If WScript.Arguments.Count = 1 Then
If FSO.FileExists(Wscript.Arguments.Item(0)) = true and FSO.FolderExists(Wscript.Arguments.Item(0)) = false Then
Alert "You dragged a file, not a folder! My god." & vbcrlf & "Script will terminate immediately", 0, "Alert: User is stupid", 48
WScript.Quit
Else
targetDir = WScript.Arguments.Item(0)
Wshso.Popup targetDir
End If
Else
targetDir = Wshso.SpecialFolders("Desktop")
Alert "Note: No folder to traverse detected, default set to:" & vbcrlf & Wshso.SpecialFolders("Desktop"), 0, "Alert", 48
End If
If it is a normal path without Unicode characters, it's fine. But in this case:
Directory: 4minute (포미닛) - Hit Your Heart
Then it will show something like 4minute (?) - Hit Your Heart
And if I do a FolderExists it can't find the dragged folder.
Is there any workaround to support Unicode named Folders?
Thanks!
I'll edit if this is not clear enough
This does seem to be a problem peculiar to the Windows Script Host's DropHandler shell extension. Whereas:
test.vbs "C:\포미닛.txt"
C:\WINDOWS\System32\WScript.exe "test.vbs" "C:\포미닛.txt"
both work when typed from the console (even if the console can't render the Hangul so it looks like ?), a drag and drop operation that should result in the same command goes through a Unicode->ANSI->Unicode translation that loses all characters that aren't in the current ANSI code page. (So 포미닛 will work on a default Korean Windows install but not Western.)
I'm not aware of a proper way to fix the problem. You could perhaps work around it by changing the DropHandler for .vbs files in the registry:
HKEY_CLASSES_ROOT\VBSFile\ShellEx\DropHandler\(Default)
from the WSH DropHandler ({60254CA5-953B-11CF-8C96-00AA00B8708C}) to {86C86720-42A0-1069-A2E8-08002B30309D}, the one used for .exe, .bat and similar, which doesn't suffer from this issue. You would also probably have to change the file association for .vbs to put quotes around the filename argument too, since the EXE DropHandler doesn't, to avoid problems with spaces in filenames.
Since this affects argument-passing for all VBS files it would be a perilous fix to deploy on any machine but your own. If you needed to do that, maybe you could try creating a new file extension with the appropriate DropTarget rather than changing VBSFile itself? Or maybe forgo drop-onto-script behaviour and provide a file Open dialog or manual drop field instead.
For anyone landing here from Google...
Bobince's tip lead me to work around this problem by wrapping my vbscript file (myscript.vbs) in a dos batch file (mybatch.bat).
The tip was:
"Seem to be a problem peculiar to the Windows Script Host's
DropHandler shell extension whereas.... the one used for .exe, .bat and
similar... doesn't suffer from this issue."
mybatch.bat contains:
:Loop
IF "%1"=="" GOTO Continue
set allfiles=%allfiles% "%1"
SHIFT
GOTO Loop
:Continue
"myscript.vbs" %allfiles%
You may also find this code from my myscript.vbs to be helpful
For Each strFullFileName In Wscript.Arguments
' do stuff
Next
Based on DG's answer, if you just want to accept one file as drop target then you can write a batch file (if you have it named as "x.bat" place VBScript with filename "x.bat.vbs" at same folder) that just contains:
#"%0.vbs" %1
the # means to not output the row on the display (I found it to show garbage text even if you use chcp 1250 as first command)
don't use double-quotes around %1, it won't work if your VBScript uses logic like the following (code I was using below was from http://jeffkinzer.blogspot.com/2012/06/vbscript-to-convert-excel-to-csv.html). Tested it and it works fine with spaces in the file and folder names:
Dim strExcelFileName
strExcelFileName = WScript.Arguments.Item(0) 'file name to parse
' get path where script is running
strScript = WScript.ScriptFullName
Dim fso
Set fso = CreateObject ("Scripting.FileSystemObject")
strScriptPath = fso.GetAbsolutePathName(strScript & "\..")
Set fso = Nothing
' If the Input file is NOT qualified with a path, default the current path
LPosition = InStrRev(strExcelFileName, "\")
if LPosition = 0 Then 'no folder path
strExcelFileName = strScriptPath & "\" & strExcelFileName
strScriptPath = strScriptPath & "\"
else 'there is a folder path, use it for the output folder path also
strScriptPath = Mid(strExcelFileName, 1, LPosition)
End If
' msgbox LPosition & " - " & strExcelFileName & " - " & strScriptPath
Modify WSH DropHandler ({60254CA5-953B-11CF-8C96-00AA00B8708C}) to {86C86720-42A0-1069-A2E8-08002B30309D} and add this function to convert short path to long:
Function Short2Long(shortFullPath)
dim fs
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.GetFile(shortFullPath)
Set app = CreateObject("Shell.Application")
Short2Long = app.NameSpace(f.ParentFolder.Path).ParseName(f.Name).Path
end function

Vim: change formatting of variables in a script

I am using vim to edit a shell script (did not use the right coding standard). I need to change all of my variables from camel-hum-notation startTime to caps-and-underscore-notation START_TIME.
I do not want to change the way method names are represented.
I was thinking one way to do this would be to write a function and map it to a key. The function could do something like generating this on the command line:
s/<word under cursor>/<leave cursor here to type what to replace with>
I think that this function could be applyable to other situations which would be handy. Two questions:
Question 1: How would I go about creating that function.
I have created functions in vim before the biggest thing I am clueless about is how to capture movement. Ie if you press dw in vim it will delete the rest of a word. How do you capture that?
Also can you leave an uncompleted command on the vim command line?
Question 2: Got a better solution for me? How would you approach this task?
Use a plugin
Check the COERCION section at the bottom of the page:
http://www.vim.org/scripts/script.php?script_id=1545
Get the :s command to the command line
:nnoremap \c :%s/<C-r><C-w>/
<C-r><C-w> gets the word under the cursor to command-line
Change the word under the cursor with :s
:nnoremap \c lb:s/\%#<C-r><C-w>/\=toupper(substitute(submatch(0), '\<\#!\u', '_&', 'g'))/<Cr>
lb move right, then to beginning of the word. We need to do this to get
the cursor before the word we wish to change because we want to change only
the word under the cursor and the regex is anchored to the current cursor
position. The moving around needs to be done because b at the
start of a word moves to the start of the previous word.
\%# match the current cursor position
\= When the substitute string starts with "\=" the remainder is interpreted as an expression. :h sub-replace-\=
submatch(0) Whole match for the :s command we are dealing with
\< word boundary
\#! do not match the previous atom (this is to not match at the start of a
word. Without this, FooBar would be changed to _FOO_BAR)
& in replace expressions, this means the whole match
Change the word under the cursor, all matches in the file
:nnoremap \a :%s/<C-r><C-w>/\=toupper(substitute(submatch(0), '\<\#!\u', '_&', 'g'))/g<Cr>
See 3. for explanation.
Change the word under the cursor with normal mode commands
/\u<Cr> find next uppercase character
i_ insert an underscore.
nn Search the last searched string twice (two times because after exiting insert mode, you move back one character).
. Repeat the last change, in this case inserting the underscore.
Repeat nn. until all camelcases have an underscore added before them, that is, FooBarBaz has become Foo_Bar_Baz
gUiw uppercase current inner word
http://vim.wikia.com/wiki/Converting_variables_to_camelCase
I am not sure what you understand under 'capturing movements'. That
said, for a starter, I'd use something like this for the function:
fu! ChangeWord()
let l:the_word = expand('<cword>')
" Modify according to your rules
let l:new_var_name = toupper(l:the_word)
normal b
let l:col_b = col(".")
normal e
let l:col_e = col(".")
let l:line = getline(".")
let l:line = substitute(
\ l:line,
\ '^\(' . repeat('.', l:col_b-1) . '\)' . repeat('.', l:col_e - l:col_b+1),
\ '\1' . l:new_var_name,
\ '')
call setline(".", l:line)
endfu
As to leaving an uncompleted command on the vim command line, I think you're after
:map ,x :call ChangeWord(
which then can be invoked in normal mode by pressing ,x.
Update
After thinking about it, this following function is a bit shorter:
fu! ChangeWordUnderCursor()
let l:the_word = expand('<cword>')
"" Modify according to your rules
let l:new_var_name = '!' . toupper(l:the_word) . '!'
normal b
let l:col_b = col(".")
normal e
let l:col_e = col(".")
let l:line = getline(".")
exe 's/\%' . l:col_b . 'c.*\%' . (l:col_e+1) .'c/' . l:new_var_name . '/'
endfu