Below is a one-line script I am writing that is causing problems when I try to add an open-paran within invoke-expression.

Within the script, the file map.txt contains 800 lines of 3 digit numbers. The script is intended to change the name of existing files that start with 001..400 (unique/non-repeating) by prefixing a new (unique) three digit number and space before the existing file. To do this, the a three-digit number is extracted from map.txt to find the file I want renamed (using invoke-expression "ls $line1 (*" and the next line in map.txt is the number that should be prefixed to the existing file. So, if the first two lines of map.txt are 001 and 008, the file, 001 (additional characters).ext (parentheses are part of file name) will be changed to 008 001 (additional characters).ext

It will execute if I remove the open-paran within invoke-expression. However, without the open-paran within invoke-expression, when the map.txt file comes to a number pair of 008 021, it will try to rename the 008 001 (additional characters).ext that has already been renamed. Adding the open-paren makes files names unique regardless of name changes that have already occurred and the script should execute as desired. That is, if I can figure out how to include an open-paren within invoke expression. I could probably do a work around with a few lines of regex and -match but if I can find a quick solution to include the open-paran with invoke-expression, it would be ideal.

Thanks for your help.

for ($i=0; $i -lt 800; $i=($i+"2")) {$line1=(get-content ..\map.txt | select -index $i); $line2=(get-content ..\map.txt | select -index ($i+"1")); $old=(invoke-expression 'ls $line1 (*'); $fileold=($old.basename+$old.extension); $filenew=($line2+" "+$fileold); mv "$fileold" "$filenew"} 
  • I would still love a solution. But, I found a quick work around. Using invoke-expression "ls $line1*" and creating a new empty subfolder (I called new), I changed the move command to mv $fileold" "new\$filenew" preventing the script from being able to act upon a file that has already been renamed. – Brian Jul 23 '20 at 2:29
  • 1
    [1] why use the dangerous Invoke-Expression cmdlet when you can simply rename the file with standard PoSh commands? the I-E cmdlet is considered the equivalent of SQL injection in terms of risk. [2] the term "one liner" means without any line delimiters. you have 6 lines inside your for loop. [grin] [3] why are you quoting the integers you are adding to your $I variable? that is dangerous since you are adding a char to an int. yes, PoSh coerces it to an int ... but why use a char for that? – Lee_Dailey Jul 23 '20 at 14:49
  • @Lee_Dailey, unless you can add something that is useful you are wasting everyone's time. Not one thing you said helped to address the problem I posed. I would certainly be open to alternative approaches if you had presented any. You clearly like to criticize but not critically criticize as you offer no solutions to make something better. I imagine this is because you aren't loved and feel better about yourself by trying to put down others. As for "one-liners," the accepted definition isn't literally one line of code. See: unix.stackexchange.com/questions/544428/one-liner-vs-script – Brian Jul 23 '20 at 23:38
  • you stated that you had a fix, so i was addressing other apparent problems - a critique is what was intended. if you don't want that sort of commentary on your code, flag my comment for removal OR for for moderator attention. – Lee_Dailey Jul 24 '20 at 2:36
  • @Brian Speaking of wasting everyones time... $line1="001";Invoke-Expression 'ls $line1 (*' would have been a MCRE and nobody would have ever had to read/understand your file renaming stuff as it is not part of the problem. Anyway, maybe you can get some inspiration from my code below. – Thomas Jul 24 '20 at 20:50

To make Invoke-Expression 'ls $line1 (*' or Invoke-Expression "ls $line1 (*" work as intended, quote the line you want to pass to ls with different quotes, like Invoke-Expression 'ls "$line1 (*"' or Invoke-Expression "ls '$line1 (*'".

Furthermore, I would like to recommend the following code optimization to you:

$numbers = Get-Content -Path "..\map.txt"
for ($i = 0; $i -lt 400; $i++) {
    Get-Item -Path "$($numbers[2*$i]) (*" | ForEach-Object -Process {Rename-Item -Path $_.Name -NewName "$($numbers[2*$i+1]) $($_.Name)"}

And if you want to squeeze that into one line, you can do it like this:

$n=gc ..\map.txt;for($i=0;$i-lt400;$i++){gi "$($n[2*$i]) (*"|%{rni $_ "$($n[2*$i+1]) $($_.Name)"}}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.