I need some help in TCL. I have the following string as an example:
%3Cdiv%20id%3D%22video-container-direct%22%20style%3D%22position%3A%20relative%3Bpadding-bottom%3A%2056.6%25%3Bpadding-top%3A%2090px
%3Bheight%3A%200%3Boverflow%3A%20hidden%3B%22%3E%3Ciframe%20allowfullscreen%3D%22%22%20frameborder%3D%220%22%20scrolling%3D%E2%80%9Cno
%22%20src%3D%22%2F%2Fwww.vsports.pt%2Fembd%2F68680%2Fm%2F8856%2Fmaisf%2Fce1702fde64b1cf799dded7f1f3ab428%3Fautostart%3Dfalse%22%20
style%3D%22position%3A%20absolute%3Btop%3A0%3Bleft%3A%200%3Bwidth%3A%20100%25%3Bheight%3A%20100%25%3Bborder%3A0%22%3E%3C%2Fiframe
%3E%3C%2Fdiv%3E
and I want to replace everything after % and removing the % of course. I mean, turning %2F to /, %3D to =, and so on.
Easy way is using the uri::urn package from tcllib (Should be installable via your OS's package manager):
#!/usr/bin/env tclsh
package require uri::urn
set s "%3Cdiv%20id%3D%22video-container-direct%22%20style%3D%22position%3A%20relative%3Bpadding-bottom%3A%2056.6%25%3Bpadding-top%3A%2090px
%3Bheight%3A%200%3Boverflow%3A%20hidden%3B%22%3E%3Ciframe%20allowfullscreen%3D%22%22%20frameborder%3D%220%22%20scrolling%3D%E2%80%9Cno
%22%20src%3D%22%2F%2Fwww.vsports.pt%2Fembd%2F68680%2Fm%2F8856%2Fmaisf%2Fce1702fde64b1cf799dded7f1f3ab428%3Fautostart%3Dfalse%22%20
style%3D%22position%3A%20absolute%3Btop%3A0%3Bleft%3A%200%3Bwidth%3A%20100%25%3Bheight%3A%20100%25%3Bborder%3A0%22%3E%3C%2Fiframe
%3E%3C%2Fdiv%3E"
puts [uri::urn::unquote $s]
Tcl 8.7 will let you do it with regsub (URL decoding is even one of the examples, copied below):
# Match one of the sequences in a URL-encoded string that needs
# fixing, converting + to space and %XX to the right character
# (e.g., %7e becomes ~)
set RE {(\+)|%([0-9A-Fa-f]{2})}
# Note that -command uses a command prefix, not a command name
set decoded [regsub -all -command $RE $string {apply {{- p h} {
# + is a special case; handle directly
if {$p eq "+"} {
return " "
}
# convert hex to a char
scan $h %x charNumber
format %c $charNumber
}}}]
puts $decoded
Related
I have a file file.dat as follow:
1.1,2.1 1.4
3.1,2.1 2.4
2.4,4.5 11.5
..
And I want to select each time the whole line (string) and replace it in another file. So far I tried the following
#!/bin/csh
set FILENAME = 'file.dat' # file in which the strings are
set str = "229.8,230.9 230.36" # initialize the first string
set n = 1
while ( $n <= 3 ) # number of lines in the FILENAME
echo Testing the first string $str
set rep = $(head -n $n "$FILENAME")
# n++ # increment the index
end
When I tried to launch the script csh launch.sh I obtained the follow error message
Testing the first string 229.8,230.9 230.36
Illegal variable name. # connect with the rep definition(?)
The file in which I want to change the string str is as follow (this is btw a secondary problem which I could figure out by myself once I understand what's wrong in the first lines):
# Name Type Par
Mi FI 154.2355189465
So UN 229.8,230.9 230.36 # line to be changed
Za FI 0.8000020209
May somebody help me, please?
$(...) is Bash syntax for command substitution in Bash.
In C-shell you have to use backticks instead (yuck).
I'm looking for a standard tool capable of taking all of its arguments and turning it into a single string suitable for use as multiple arguments in an automatically generated bash/sh/zsh script. Such a command is extremely useful in various disciplines of script-fu. An example of its usage:
% shsafe 'A big \nasty string '\'' $HOME $PATH' 'another string \\'
'A big \nasty string '\'' $HOME $PATH' 'another string \\'
Using it in another script:
% sshc host rm 'file/with spaces and $special chars'
where sshc contains
#!/bin/bash
# usage: sshc host command [arg ...]
# Escapes its arguments so that the command may contain special
# characters. Assumes the remote shell is sh-like.
host=$1
shift
exec ssh "$host" "$(shsafe "$#")"
Another example:
#!/bin/bash
# Run multiple commands in a single sudo session. The arguments of
# this script are passed as arguments to the first command. Useful if
# you don't want to have to type the password for both commands and
# the first one takes a while to run.
sudo bash -c "pacman -Syu $(shsafe "$#") && find /etc -name '*.pacnew'"
I couldn't find a suitable solution to this problem in the pre-existing commands, so I made up my own, called shsafe. It uses the fact that single quotes, '', turn off absolutely all shell expansion, except for ' itself.
shsafe:
#!/usr/bin/env python
from sys import *
n = len(argv)
if n == 1:
exit(0)
i = 1
while True:
stdout.write("'" + argv[i].replace("'", "'\\''") + "'")
i += 1
if i == n:
break
stdout.write(' ')
stdout.write('\n')
Is there any standard tool capable of doing this to its arguments?
Note that the printf command with a format string consisting of just the %q formatter is not good enough for this, because it won't keep multiple arguments separated:
% printf %q arg1 arg2
arg1arg2
I did eventually figure out a decent way of doing this:
% printf "$'%q' " 'crazy string \ $HOME' 'another\ string'
$'crazy\ string\ \\\ \$HOME' $'another\\\ string'
It's a little error prone what with the quotes everywhere, so it's not ideal, IMO, but it's a solid solution that should work anywhere. If it's being used a lot, you could always turn it into a shell function:
shsafe () {
printf "$'%q' " "$#"
}
I want to process some twitter data sets with a perl script. The file is in a csv format.
I want to remove self addressing mentions
the csv column and data is this way for example
user, mention(user), message
vims789, vnjuei234, yea this is good
dfion, youwen12, this is win
don234, don234, this is green
wen123, tileas, this is blue
The duplicate which is "don234, don234" mentioning itself, the line should be deleted. Example
user, mention(user), message
vims789, vnjuei234, yea this is good
dfion, youwen12, this is win
wen123, tileas, this is blue
Maybe something like this:
#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV;
my $csv = Text::CSV->new();
while ( my $row = $csv->getline( \*DATA ) ) {
my ( $user, $mention, $message ) = #$row;
print $message,"\n" unless $user eq $mention;
}
__DATA__
user, mention(user), Message
vims789, vnjuei234, yea this is good
dfion, youwen12, this is win
don234, don234, this is green
wen123, tileas, this is blue
You can do this very quickly with a back-reference. Since you want to find something, a comma, some space, and then that something again, assuming that the string will be all word characters, this should work:
my $regex
= qr{ ^ # beginning of the line
(\w+) # A "word"
, # A comma
\s+ # space
\1 # a back reference to the first capture.
\b # demand that it end the sequence of word characters.
}x;
my #filtered_lines = grep { !m/$regex/ } #lines;
Whatever you want to call it, I'm trying to figure out a way to take the contents of an existing string and evaluate them as a double-quoted string. For example, if I create the following strings:
$string = 'The $animal says "meow"'
$animal = 'cat'
Then, Write-Host $string would produce The $animal says "meow". How can I have $string re-evaluated, to output (or assign to a new variable) The cat says "meow"?
How annoying...the limitations on comments makes it very difficult (if it's even possible) to include code with backticks. Here's an unmangled version of the last two comments I made in response to zdan below:
----------
Actually, after thinking about it, I realized that it's not reasonable to expect The $animal says "meow" to be interpolated without escaping the double quotes, because if it were a double-quoted string to begin with, the evaluation would break if the double quotes weren't escaped. So I suppose the answer would be that it's a two step process:
$newstring = $string -replace '"', '`"'
iex "`"$string`""
One final comment for posterity: I experimented with ways of getting that all on one line, and almost anything that you'd think works breaks once you feed it to iex, but this one works:
iex ('"' + ($string -replace '"', '`"') + '"')
Probably the simplest way is
$ExecutionContext.InvokeCommand.ExpandString($var)
You could use Invoke-Expression to have your string reparsed - something like this:
$string = 'The $animal says `"meow`"'
$animal = 'cat'
Invoke-Expression "Write-Host `"$string`""
Note how you have to escape the double quotes (using a backtick) inside your string to avoid confusing the parser. This includes any double quotes in the original string.
Also note that the first command should be a command, if you need to use the resulting string, just pipe the output using write-output and assign that to a variable you can use later:
$result = Invoke-Expression "write-output `"$string`""
As noted in your comments, if you can't modify the creation of the string to escape the double quotes, you will have to do this yourself. You can also wrap this in a function to make it look a little clearer:
function Invoke-String($str) {
$escapedString = $str -replace '"', '`"'
Invoke-Expression "Write-Output `"$escapedString`""
}
So now it would look like this:
# ~> $string = 'The $animal says "meow"'
# ~> $animal = 'cat'
# ~> Invoke-String $string
The cat says "meow"
You can use the -f operator. This is the same as calling [String]::Format as far as I can determine.
PS C:\> $string = 'The {0} says "meow"'
PS C:\> $animal = 'cat'
PS C:\> Write-Host ($string -f $animal)
The cat says "meow"
This avoids the pitfalls associated with quote stripping (faced by ExpandString and Invoke-Expression) and arbitrary code execution (faced by Invoke-Expression).
I've tested that it is supported in version 2 and up; I am not completely certain it's present in PowerShell 1.
Edit: It turns out that string interpolation behavior is different depending on the version of PowerShell. I wrote a better version of the xs (Expand-String) cmdlet with unit tests to deal with that behavior over here on GitHub.
This solution is inspired by this answer about shortening calls to object methods while retaining context. You can put the following function in a utility module somewhere, and it still works when you call it from another module:
function xs
{
[CmdletBinding()]
param
(
# The string containing variables that will be expanded.
[parameter(ValueFromPipeline=$true,
Position=0,
Mandatory=$true)]
[string]
$String
)
process
{
$escapedString = $String -replace '"','`"'
$code = "`$ExecutionContext.InvokeCommand.ExpandString(`"$escapedString`")"
[scriptblock]::create($code)
}
}
Then when you need to do delayed variable expansion, you use it like this:
$MyString = 'The $animal says $sound.'
...
$animal = 'fox'
...
$sound = 'simper'
&($MyString | xs)
&(xs $MyString)
PS> The fox says simper.
PS> The fox says simper.
$animal and $sound aren't expanded until the last two lines. This allows you to set up a $MyString up front and delay expansion until the variables have the values you want.
Invoke-Expression "`"$string`""
I have a variable that is entered at a prompt:
my $name = <>;
I want to append a fixed string '_one'to this (in a separate variable).
E.g. if $name = Smith then it becomes 'Smith_one'
I have tried several various ways which do not give me the right results, such as:
my $one = "${name}_one";
^ The _one appears on the next line when I print it out and when I use it, the _one is not included at all.
Also:
my $one = $name."_one";
^ The '_one' appears at the beginning of the string.
And:
my $end = '_one';
my $one = $name.$end;
or
my $one = "$name$end";
None of these produce the result I want, so I must be missing something related to how the input is formatted from the prompt, perhaps. Ideas appreciated!
Your problem is unrelated to string appending: When you read a line (e.g. via <>), then the record input separator is included in that string; this is usually a newline \n. To remove the newline, chomp the variable:
my $name = <STDIN>; # better use explicit filehandle unless you know what you are doing
# now $name eq "Smith\n"
chomp $name;
# now $name eq "Smith"
To interpolate a variable into a string, you usually don't need the ${name} syntax you used. These lines will all append _one to your string and create a new string:
"${name}_one" # what you used
"$name\_one" # _ must be escaped, else the variable $name_one would be interpolated
$name . "_one"
sprintf "%s_one", $name
# etc.
And this will append _one to your string and still store it in $name:
$name .= "_one"