Powershell, modify TXT/XML file to add a string after a specific variable? - powershell

Trying to edit the DyanmoSettings.xml file without installing any specific packages on the Windows systems (sed, awk, etc)
to do:
need to modify this file;
%appdata%\dynamo\Dynamo Revit\2.3\DynamoSettings.xml
and within that file above, find this line;
>
> <CustomPackageFolders>
> <string>C:\Users\user1.mydomain\AppData\Roaming\Dynamo\Dynamo Revit\2.3</string>
> </CustomPackageFolders>
>
and add 'C:\Users%USERNAME%\OneDrive\DT-s\Revit Scripts\Dynamo\Custom Packages '
so it looks like this;
>
> <CustomPackageFolders>
> <string>C:\Users\user1.mydomain\AppData\Roaming\Dynamo\Dynamo Revit\2.3</string>
> <string>C:\Users\%USERNAME%\OneDrive\DT-s\Revit Scripts\Dynamo\Custom Packages </string>
> </CustomPackageFolders>
>
>
TIA!
tried using standard >> method in CMD but this didn't work.

Powershell uses c#. So any c# code can run inside powershell without any installation. Code below uses the c# library Xml Linq. Try following
using assembly System
using assembly System.Xml.Linq
$Filename = "c:\temp\test.xml"
$xDoc = [System.Xml.Linq.XDocument]::Load($Filename)
$customFolder = $xDoc.Descendants("CustomPackageFolders")
$newElement = [System.Xml.Linq.XElement]::new("string","C:\Users\%USERNAME%\OneDrive\DT-s\Revit Scripts\Dynamo\Custom Packages")
$customFolder.Add($newElement)
Write-Host "customFolder = " $customFolder
$xDoc.Save("c:\temp\test1.xml")

If that xml file looks anything like this
<DynamOconfig>
<CustomPackageFolders>
<string>C:\Users\user1.mydomain\AppData\Roaming\Dynamo\Dynamo Revit\2.3</string>
</CustomPackageFolders>
</DynamOconfig>
then
# load the settings file
$xml = [System.Xml.XmlDocument]::new()
$xml.Load("$env:APPDATA\dynamo\Dynamo Revit\2.3\DynamoSettings.xml")
# create a new <string> node
$newNode = $xml.CreateElement('string')
$newNode.InnerText = 'C:\Users\%USERNAME%\OneDrive\DT-s\Revit Scripts\Dynamo\Custom Packages'
# find the <CustomPackageFolders> node and append the new node to it
[void]$xml.SelectSingleNode('//CustomPackageFolders').AppendChild($newNode)
# save to (for safety NEW) file
$xml.Save("$env:APPDATA\dynamo\Dynamo Revit\2.3\NEW_DynamoSettings.xml")
Result:
<DynamOconfig>
<CustomPackageFolders>
<string>C:\Users\user1.mydomain\AppData\Roaming\Dynamo\Dynamo Revit\2.3</string>
<string>C:\Users\%USERNAME%\OneDrive\DT-s\Revit Scripts\Dynamo\Custom Packages</string>
</CustomPackageFolders>
</DynamOconfig>
P.S. If your intention is to have %USERNAME% interpolated in the string so it expands to your username, then create the new node's innertext like ths:
$newNode.InnerText = "C:\Users\$env:USERNAME\OneDrive\DT-s\Revit Scripts\Dynamo\Custom Packages"
(mind you need double-quotes now)

Related

Output file not created when running a R command in a Nextflow file?

I am trying to run a nextflow pipeline but the output file is not created.
The main.nf file looks like this:
#!/usr/bin/env nextflow
nextflow.enable.dsl=2
process my_script {
"""
Rscript script.R
"""
}
workflow {
my_script
}
In my nextflow.config I have:
process {
executor = 'k8s'
container = 'rocker/r-ver:4.1.3'
}
The script.R looks like this:
FUN <- readRDS("function.rds");
input = readRDS("input.rds");
output = FUN(
singleCell_data_input = input[[1]], savePath = input[[2]], tmpDirGC = input[[3]]
);
saveRDS(output, "output.rds")
After running nextflow run main.nf the output.rds is not created
Nextflow processes are run independently and isolated from each other from inside the working directory. For your script to be able to find the required input files, these must be localized inside the process working directory. This should be done by defining an input block and declaring the files using the path qualifier, for example:
params.function_rds = './function.rds'
params.input_rds = './input.rds'
process my_script {
input:
path my_function_rds
path my_input_rds
output:
path "output.rds"
"""
#!/usr/bin/env Rscript
FUN <- readRDS("${my_function_rds}");
input = readRDS("${my_input_rds}");
output = FUN(
singleCell_data_input=input[[1]], savePath=input[[2]], tmpDirGC=input[[3]]
);
saveRDS(output, "output.rds")
"""
}
workflow {
function_rds = file( params.function_rds )
input_rds = file( params.input_rds )
my_script( function_rds, input_rds )
my_script.out.view()
}
In the same way, the script itself would need to be localized inside the process working directory. To avoid specifying an absolute path to your R script (which would not make your workflow portable at all), it's possible to simply embed your code, making sure to specify the Rscript shebang. This works because process scripts are not limited to Bash1.
Another way, would be to make your Rscript executable and move it into a directory called bin in the the root directory of your project repository (i.e. the same directory as your 'main.nf' Nextflow script). Nextflow automatically adds this folder to the $PATH environment variable and your script would become automatically accessible to each of your pipeline processes. For this to work, you'd need some way to pass in the input files as command line arguments. For example:
params.function_rds = './function.rds'
params.input_rds = './input.rds'
process my_script {
input:
path my_function_rds
path my_input_rds
output:
path "output.rds"
"""
script.R "${my_function_rds}" "${my_input_rds}" output.rds
"""
}
workflow {
function_rds = file( params.function_rds )
input_rds = file( params.input_rds )
my_script( function_rds, input_rds )
my_script.out.view()
}
And your R script might look like:
#!/usr/bin/env Rscript
args <- commandArgs(trailingOnly = TRUE)
FUN <- readRDS(args[1]);
input = readRDS(args[2]);
output = FUN(
singleCell_data_input=input[[1]], savePath=input[[2]], tmpDirGC=input[[3]]
);
saveRDS(output, args[3])

Powershell script not working when included in Jenkins Pipeline Script

What I am trying to achieve with Powershell is as follows:
Increment the Build Number in the AssemblyInfo.cs file on the Build Server. My Script looks like below right now after over a 100 iterations of different variations I am still unable to get it to work. The script works well in the Powershell console but when included into the Jenkins Pipeline Script I get various errors that are proving hard to fix...
def getVersion (file) {
def result = powershell(script:"""Get-Content '${file}' |
Select-String '[0-9]+\\.[0-9]+\\.[0-9]+\\.' |
foreach-object{$_.Matches.Value}.${BUILD_NUMBER}""", returnStdout: true)
echo result
return result
}
...
powershell "(Get-Content ${files[0].path}).replace('[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+',
${getVersion(files[0].path)})) } | Set-Content ${files[0].path}"
...
How about a groovy approach (with Jenkins keywords) instead of PowerShell:
def updtaeAssemblyVersion() {
def files = findFiles(glob: '**/AssemblyInfo.cs')
files.each {
def content = readFile file: it.path
def modifedContent = content.repalceAll(/([0-9]+\\.[0-9]+\\.[0-9]+\\.)([0-9]+)/,"\$1${BUILD_NUMBER}")
writeFile file: it.path, text: modifedContent
}
}
It will read all relevant files and replace only the build section of the version for every occurrence that matches the version regex.

How to kick off ExtendScript JSX script from Powershell

I want to be able to execute an Adobe Illustrator ExtendScript via Windows Powershell. I believe this should be possible due to this answer that describes using VB via COM.
This is my Powershell script:
$illustratorRef = New-Object -ComObject Illustrator.Application
$conversionScript = New-Object -ComObject Scripting.FileSystemObject
$scriptFile = $conversionScript.OpenTextFile("C:\ws\ArtConversion\alert-test.jsx")
$fileContents = $scriptFile.ReadAll()
$scriptFile.Close()
$fileToRun = $fileContents + "main(arguments)"
$args = "line1", "line2"
$illustratorRef.DoJavaScript($fileToRun, $args, 1)
Here is the alert-test.jsx script:
function main(argv) {
alert('message: ' + argv[0]);
return argv[0];
}
Running the Powershell script opens Illustrator, but throws the following error upon encountering $illustratorRef.DoJavaScript:
Library not registered. (Exception from HRESULT: 0x8002801D (TYPE_E_LIBNOTREGISTERED))
I am using Adobe Illustrator 2019 CC (64bit) and Powershell 5.1.16299.666
I achieved my goal, but wasn't able to do it 100% with Powershell.
The 2017 Adobe Illustrator Scripting Guide contains this statement on page 22:
In VBScript, there are several ways to create an instance of Illustrator.
When referring to JavaScript however, it says:
Information on launching Illustrator from JavaScript is beyond the scope of this guide.
I couldn't find any official documentation on how to programmatically start Illustrator on Windows using other languages besides VB, so I ended up letting my Powershell script handle the heavy lifting of directory traversal and logging, while having it open Illustrator by means of a Visual Basic script.
The call from Powershell into VB looks like this:
$convertFile = "cmd /C cscript .\run-illustrator-conversion.vbs $arg1, $arg2"
$output = Invoke-Expression $convertFile
The VB script ended up looking like this:
Dim appRef
Dim javaScriptFile
Dim argsArr()
Dim fsObj : Set fsObj = CreateObject("Scripting.FileSystemObject")
Dim jsxFile : Set jsxFile = fsObj.OpenTextFile(".\script-to-run.jsx", 1, False)
Dim fileContents : fileContents = jsxFile.ReadAll
jsxFile.Close
Set jsxFile = Nothing
Set fsObj = Nothing
javascriptFile = fileContents & "main(arguments);"
Set appRef = CreateObject("Illustrator.Application.CS5")
ReDim argsArr(Wscript.Arguments.length - 1)
For i = 0 To Wscript.Arguments.length - 1
argsArr(i) = Wscript.Arguments(i)
Next
Wscript.Echo appRef.DoJavaScript(javascriptFile, argsArr, 1)
Note: Check scripting guide to get correct string for your version of Illustrator.

Using os.path.expanduser with subprocess.call

I am having some trouble with subprocess.call and os.path.expanduser.
I need to run this script on a computer other than my own, and one issue I have run into is running the script on a pc that's user name contains a space C:\User\User Example. I was told in a previous post that I should use args with a list. Howver, I don't understand what that means. Any tutorials or relevant post would be greatly appreciated.
# Set varible for gdal_calc
gdal_calc = "C:\\Program Files (x86)\\GDAL\\gdal_calc.py"
# make dictionary of environmental variables
gdal_env = os.environ.copy()
# modify and add variables
gdal_env["GDAL_DATA"] = "C:\\Program Files (x86)\\GDAL\gdal-data"
gdal_env["GDAL_DRIVER_PATH"] = "C:\\Program Files (x86)\\GDAL\\gdalplugins"
gdal_env["PATH"] = gdal_env["PATH"] + ";C:\\Program Files (x86)\\GDAL"
# Set constants
# The pathway to the images files are nested within the '--outfile=' command
inHVZero = os.path.expanduser('~\\Desktop\\Components\\Zeros\\newHVZeros_.img')
outPlace = os.path.expanduser('~\\\Desktop\\Components\\db_Files\\newHVdB.img')
outVFile = '--outfile='+ outPlace
cmd_HV = ['-A', inHVZero, outVFile, '--calc=10*log10(power(A,2))-83']
#calc_cmd_HV = ['C:\\Program Files (x86)\\GDAL\\gdal_calc.py', '-A', inHVZero, '--outfile='+outPlace, '--calc=10*log10(power(A,2))-83']
inVHZero = os.path.expanduser('~\\Desktop\\Components\\Zeros\\newVHZeros_.img')
outPlace_1 = os.path.expanduser('~\\Desktop\\Components\\db_Files\\newVHdB.img')
outVFile_1 = '--outfile='+ outPlace_1
cmd_VH = ['-A', inVHZero, outVFile_1, '--calc=10*log10(power(A,2))-83']
#calc_cmd_VH = ['C:\\Program Files (x86)\\GDAL\\gdal_calc.py', '-A', inVHZero, '--outfile='+outPlace_1, '--calc=10*log10(power(A,2))-83']
subprocess.call([sys.executable,gdal_calc] + cmd_HV, env=gdal_env)
subprocess.call([sys.executable,gdal_calc] + cmd_VH, env=gdal_env)

Get Tfs Shelveset file contents at the command prompt?

I'm interested in getting the contents of a shelveset at the command prompt. Now, you would think that a cmdlet such as Get-TfsShelveset, available in the TFS Power Tools, would do this. You might also think that "tf.exe shelvesets" would do this.
However, unless I've missed something, I'm appalled to report that neither of these is the case. Instead, each command requires you to give it a shelveset name, and then simply regurgitates a single line item for that shelveset, along with some metadata about the shelveset such as creationdate, displayname, etc. But as far as I can tell, no way to tell what's actually in the shelf.
This is especially heinous for Get-TfsShelveset, which has the ability to include an array of file descriptors along with the Shelveset object it returns. I even tried to get clever, thinking that I could harvest the file names from using -WhatIf with Restore-TfsShelveset, but sadly Restore-TfsShelveset doesn't implement -WhatIf.
Please, someone tell me I'm wrong about this!
tf status /shelveset:name
will list out the content of the named shelveset (you can also supplier an owner: see tf help status).
With the TFS PowerToy's PowerShell snapin:
Get-TfsPendingChange -Shelveset name
for the same information.
It is possible to construct a small command-line application that uses the TFS SDK, which returns the list of files contained in a given shelveset.
The sample below assumes knowledge of the Shelveset name & it's owner:
using System;
using System.IO;
using System.Collections.ObjectModel;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace ShelvesetDetails
{
class Program
{
static void Main(string[] args)
{
Uri tfsUri = (args.Length < 1) ? new Uri("TFS_URI") : new Uri(args[0]);
TfsConfigurationServer configurationServer = TfsConfigurationServerFactory.GetConfigurationServer(tfsUri);
ReadOnlyCollection<CatalogNode> collectionNodes = configurationServer.CatalogNode.QueryChildren(
new[] { CatalogResourceTypes.ProjectCollection },
false, CatalogQueryOptions.None);
CatalogNode collectionNode = collectionNodes[0];
Guid collectionId = new Guid(collectionNode.Resource.Properties["InstanceId"]);
TfsTeamProjectCollection teamProjectCollection = configurationServer.GetTeamProjectCollection(collectionId);
var vcServer = teamProjectCollection.GetService<VersionControlServer>();
Shelveset[] shelves = vcServer.QueryShelvesets(
"SHELVESET_NAME", "SHELVESET_OWNER");
Shelveset shelveset = shelves[0];
PendingSet[] sets = vcServer.QueryShelvedChanges(shelveset);
foreach (PendingSet set in sets)
{
PendingChange[] changes = set.PendingChanges;
foreach (PendingChange change in changes)
{
Console.WriteLine(change.FileName);
}
}
}
}
}
Invoking this console app & catching the outcome during execution of the powershell should be possible.
Try:
tfpt review
/shelveset:shelvesetName;userName
You may also need to add on the server option so something like:
tfpt review /shelveset:Code Review;jim
/sever:company-source
I think this is what you are looking for.
This is what I ended up with, based on pentelif's code and the technique in the article at http://akutz.wordpress.com/2010/11/03/get-msi/ linked in my comment.
function Get-TfsShelvesetItems
{
[CmdletBinding()]
param
(
[string] $ShelvesetName = $(throw "-ShelvesetName must be specified."),
[string] $ShelvesetOwner = "$env:USERDOMAIN\$env:USERNAME",
[string] $ServerUri = $(throw "-ServerUri must be specified."),
[string] $Collection = $(throw "-Collection must be specified.")
)
$getShelvesetItemsClassDefinition = #'
public IEnumerable<PendingChange> GetShelvesetItems(string shelvesetName, string shelvesetOwner, string tfsUriString, string tfsCollectionName)
{
Uri tfsUri = new Uri(tfsUriString);
TfsConfigurationServer configurationServer = TfsConfigurationServerFactory.GetConfigurationServer(tfsUri);
ReadOnlyCollection<CatalogNode> collectionNodes = configurationServer.CatalogNode.QueryChildren( new[] { CatalogResourceTypes.ProjectCollection }, false, CatalogQueryOptions.None);
CatalogNode collectionNode = collectionNodes.Where(node => node.Resource.DisplayName == tfsCollectionName).SingleOrDefault();
Guid collectionId = new Guid(collectionNode.Resource.Properties["InstanceId"]);
TfsTeamProjectCollection teamProjectCollection = configurationServer.GetTeamProjectCollection(collectionId);
var vcServer = teamProjectCollection.GetService<VersionControlServer>();
var changes = new List<PendingChange>();
foreach (Shelveset shelveset in vcServer.QueryShelvesets(shelvesetName, shelvesetOwner))
{
foreach (PendingSet set in vcServer.QueryShelvedChanges(shelveset))
{
foreach ( PendingChange change in set.PendingChanges )
{
changes.Add(change);
}
}
}
return changes.Count == 0 ? null : changes;
}
'#;
$getShelvesetItemsType = Add-Type `
-MemberDefinition $getShelvesetItemsClassDefinition `
-Name "ShelvesetItemsAPI" `
-Namespace "PowerShellTfs" `
-Language CSharpVersion3 `
-UsingNamespace System.IO, `
System.Linq, `
System.Collections.ObjectModel, `
System.Collections.Generic, `
Microsoft.TeamFoundation.Client, `
Microsoft.TeamFoundation.Framework.Client, `
Microsoft.TeamFoundation.Framework.Common, `
Microsoft.TeamFoundation.VersionControl.Client `
-ReferencedAssemblies "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.Client.dll", `
"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.Common.dll", `
"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.VersionControl.Client.dll" `
-PassThru;
# Initialize an instance of the class.
$getShelvesetItems = New-Object -TypeName "PowerShellTfs.ShelvesetItemsAPI";
# Emit the pending changes to the pipeline.
$getShelvesetItems.GetShelvesetItems($ShelvesetName, $ShelvesetOwner, $ServerUri, $Collection);
}
Spent a few days trying to do this as well, this always popped up on google so here is what I found to help future generations:
To get the contents of the shelveset (at least with Team Explorer Everywhere),
use the command: tf difference /shelveset:<Shelveset name>
That will print out the contents of the shelveset and give filenames in the form :
<Changetype>: <server file path>; C<base change number>
Shelved Change: <server file path again>;<shelveset name>
So if your file is contents/test.txt
in the shelveset shelve1 (with base revision 1), you will see :
edit: $/contents/file.txt;C1
Shelved Change: $/contents/file.txt;shelve1
After that, using the tf print command
(or view if not using TEE) on $/contents/file.txt;shelve1 should get you the contents :
tf print $/contents/file.txt;shelve1
Shows you what is in the file.txt in shelveset shelve1
If you want get shelveset changes from server by using tfs command
Using power shell:
Get-TfsPendingChange -Server http://example.com/org -Shelveset shelvsetName
Using vs commands:
c:\projects>tf shelvesets BuddyTest_23
more info about this please see here
https://learn.microsoft.com/en-us/azure/devops/repos/tfvc/shelvesets-command?view=azure-devops