I'm trying to access file created time information from files on my iPhone, when it's connected to my Windows laptop. I've put together a small PowerShell test script that succesfully does so, with one small shortcoming: the function Folder.GetDetailsOf(..., 4), which returns the file's created time, does not include seconds… (An example output would be "18/03/2017 21:58".)
How would I get Folder.GetDetailsOf() to return seconds also?
I've tried applying a different culture (locale):
Setting a new culture using the required time format;
Wrapping everything in Using-Culture, as described on the MSDN blog;
Setting the system-wide short time format (in Windows Settings => Clock, Language, and Region) to "HH:mm:ss".
None of this seems to work… However, the function Get-Date applies the new culture just fine. The main difference between these two functions, is that Folder.GetDetailsOf runs via a COM object (Shell.Application), whereas Get-Date does not. If using a different culture is indeed the way to go, then how could I apply them to COM objects?
Example script
Function Using-Culture (
[System.Globalization.CultureInfo]$culture = (throw “USAGE: Using-Culture -Culture culture -Script {scriptblock}”),
[ScriptBlock]$script= (throw “USAGE: Using-Culture -Culture culture -Script {scriptblock}”)
) {
$OldCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture
trap {
[System.Threading.Thread]::CurrentThread.CurrentCulture = $OldCulture
}
[System.Threading.Thread]::CurrentThread.CurrentCulture = $culture
Invoke-Command $script
[System.Threading.Thread]::CurrentThread.CurrentCulture = $OldCulture
}
$currentThread = [System.Threading.Thread]::CurrentThread
[System.Globalization.CultureInfo] $culture = "nl-NL"
$culture.DateTimeFormat.LongTimePattern = 'HH:mm:ss'
$culture.DateTimeFormat.ShortTimePattern = 'HH:mm:ss'
$culture.DateTimeFormat.FullDateTimePattern = 'dd MMMM, yyyy HH:mm:ss'
$currentThread.CurrentCulture = $culture
$currentThread.CurrentUICulture = $culture
Using-Culture $culture {
# This outputs the date in Dutch, using the format above;
# if the format above is changed, then Get-Date's output does too
Get-Date
$path = 'C:\Windows\System32\notepad.exe'
$shell = New-Object -COMObject Shell.Application
$folder = Split-Path $path
$file = Split-Path $path -Leaf
$shellfolder = $shell.Namespace($folder)
$shellfile = $shellfolder.ParseName($file)
# GetDetailsOf() on the other hand is oblivious to the newly set culture
$shellfolder.GetDetailsOf($shellfile, 4)
}
Why do I need this?
When copying videos off my iPhone, the files get a new Created time on my laptop, so I lose the original (actual) time. The Modified time does get copied, but it's not always correct. (Not quite sure why, this sometimes also happens when I don't do anything with the video after having shot it.)
In order to fix the Created time, I'm looking to extract the Created time from the videos while they're still on the phone, so I can write them to a file, and fetch them again later.
Any other suggestions on how to tackle this problem are of course also welcome :)
I have a bunch of csv files from which I am writing data into a particular worksheet of an existing excel file. I have the below code and it works while looping through the CSV files and writing data into the existing worksheet
$CSVs ="rpt.test1",
"rpt.test2"
foreach ($csv in $CSVs)
{
$csv_name = $csv
echo "n - - - $sav_name - - -n"
foreach ($source in $Sources)
{
$src = $source
$inputCSV = "C:\Users\xxxx\Desktop\$src.$csv_name.csv"
$Path = "C:\Users\xxxx\Desktop\$csv_name.xlsx"
### Create a new Excel Workbook with one empty sheet
#$excel = New-Object -ComObject excel.application
#$workbook = $excel.Workbooks.Add(1)
#$worksheet = $workbook.worksheets.Item(1)
# Open the Excel document and pull in the 'Play' worksheet
$excel = New-Object -Com Excel.Application
$Workbook = $Excel.Workbooks.Open($Path)
$page = 'data'
$worksheet = $Workbook.worksheets | where-object {$_.Name -eq $page}
# Delete the current contents of the page
$worksheet.Cells.Clear() | Out-Null
### Build the QueryTables.Add command
### QueryTables does the same as when clicking "Data » From Text" in Excel
$TxtConnector = ("TEXT;" + $inputCSV)
$Connector = $worksheet.QueryTables.add($TxtConnector,$worksheet.Range("A1"))
$query = $worksheet.QueryTables.item($Connector.name)
### Set the delimiter (, or ;) according to your regional settings
$query.TextFileOtherDelimiter = $Excel.Application.International(5)
### Set the format to delimited and text for every column
$query.TextFileParseType = 1
$query.TextFileColumnDataTypes = ,2 * $worksheet.Cells.Columns.Count
$query.AdjustColumnWidth = 1
### Execute & delete the import query
$query.Refresh()
$query.Delete()
$Workbook.SaveAs($Path,51)
$excel.Quit()
}
Since it is an existing excel workbook, it throws a pop-up every time a file is being over-written. Have more than 15 CSV's and clicking Yes everytime is annoying
I have tried
$excel.DisplayAlerts = FALSE
and I have tried
$excel.CheckCompatibility = $False
and pretty much anything available on the internet. I am still learning powershell and at my wits end trying to stop this. Any help would be very much appreciated
Use display alerts statement before the SaveAs call:
$excel.DisplayAlerts = $false;
$excel.ActiveWorkbook.SaveAs($xlsFile);
$excel.DisplayAlerts = $false worked for me.
I'm in the process of writing a PowerShell script to help in the process of setting up new PC's for my work. This will hopefully be used by more than just me so I'm trying to think of everything.
I have offline installers (java, flash, reader, etc) saved on our FTP server that the script downloads if a local copy hasn't already been saved in the Apps directory that gets created. Periodically the files on the FTP server will get updated as new versions of the programs are released. I want the script to have an option of checking for newer versions of the installers in case someone likes to carry around the local copies and forgets to check the server every now and then. It also will need to work in Windows 7 without any need to import additional modules unless there's an easy way to do that on multiple PC's at a time. I know about the import command, but the experiences I've had needed me to copy the module files into multiple places on the PC before it'd work.
Right now I haven't had much luck finding any solutions. I've found code that checks for modified dates on local files, or files on a local server, but nothing that deals with FTP other than uploading\downloading files.
Here's the last thing I tried. I tried a combination of what I found for local files with FTP. Didn't work too well.
I'm new to PowerShell, but I've been pretty good at piecing this whole thing together so far. However, this idea is becoming troublesome.
Thank you for the help.
$ftpsite = "ftp://ftpsite.com/folder/"
$firefox = (Get-Item $dir\Apps\install_firefox.exe).LastWriteTime.toString("MM/dd/yyyy")
if ($firefoxftp = (Get-ChildItem $ftpsite/install_firefox.exe | Where{$_.LastWriteTime -gt $firefox})) {
$File = "$dir\Apps\install_firefox.exe"
$ftp = "ftp://ftpsite.com/folder/install_firefox.exe"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.DownloadFile($uri, $File)
}
UPDATE:
Here's what I have after Martin's help. It kind of works. It downloads the file from FTP, but it's not comparing the remote and local correctly. The remote file returns 20150709140505 and the local file returns 07/09/2015 2:05:05 PM. How do I format one to look like the other before the comparison, and is "-gt" the correct comparison to use?
Thanks!
function update {
$ftprequest = [System.Net.FtpWebRequest]::Create("ftp://ftpsite.com/Script_Apps/install_firefox.exe")
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
$response = $ftprequest.GetResponse().StatusDescription
$tokens = $response.Split(" ")
$code = $tokens[0]
$localfile = (Get-Item "$dir\Apps\install_firefox.exe").LastWriteTimeUtc
if ($tokens -gt $localfile) {
write-host "Updating Firefox Installer..."
$File = "$dir\Apps\install_firefox.exe"
$ftp = "ftp://ftpsite.com/Script_Apps/install_firefox.exe"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.DownloadFile($uri, $File)
"Updated Firefox" >> $global:logfile
mainmenu
}
else {
Write-Host "Local Copy is Newer."
sleep 3
mainmenu
}
}
UPDATE 2:
Seems to be working! Here's the code. Thanks for the help!
function update {
$ftprequest = [System.Net.FtpWebRequest]::Create("ftp://ftpserver.com/Script_Apps/install_firefox.exe")
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
$response = $ftprequest.GetResponse().StatusDescription
$tokens = $response.Split(" ")
$code = $tokens[0]
$localtime = (Get-Item "$dir\Apps\install_firefox.exe").LastWriteTimeUtc
if ($code -eq 213) {
$tokens = $tokens[1]
$localtime = "{0:yyyymmddHHmmss}" -f [datetime]$localtime
}
if ($tokens -gt $localtime) {
write-host "Updating Firefox Installer..."
$File = "$dir\Apps\install_firefox.exe"
$ftp = "ftp://ftpserver.com/Script_Apps/install_firefox.exe"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.DownloadFile($uri, $File)
"Updated Firefox" >> $global:logfile
mainmenu
}
else {
Write-Host "Local Copy is Newer."
sleep 3
mainmenu
}
}
You cannot use the WebClient class to check remote file timestamp.
You can use the FtpWebRequest class with its GetDateTimestamp FTP "method" and parse the UTC timestamp string it returns. The format is specified by RFC 3659 to be YYYYMMDDHHMMSS[.sss].
That would work only if the FTP server supports MDTM command that the method uses under the cover (most servers do, but not all).
$url = "ftp://ftpsite.com/folder/install_firefox.exe"
$ftprequest = [System.Net.FtpWebRequest]::Create($url)
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
$response = $ftprequest.GetResponse().StatusDescription
$tokens = $response.Split(" ")
$code = $tokens[0]
if ($code -eq 213)
{
Write-Host "Timestamp is" $tokens[1]
}
else
{
Write-Host "Error" $response
}
It would output something like:
Timestamp is 20150709065036
Now you parse it, and compare against a UTC timestamp of a local file:
(Get-Item "install_firefox.exe").LastWriteTimeUtc
Or save yourself some time and use an FTP library/tool that can do this for you.
For example with WinSCP .NET assembly, you can synchronize whole remote folder with installers with a local copy with one call to the Session.SynchronizeDirectories. Or your can limit the synchronization to a single file only.
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Ftp
$sessionOptions.HostName = "ftpsite.com"
$session = New-Object WinSCP.Session
# Connect
$session.Open($sessionOptions)
$transferOptions = New-Object WinSCP.TransferOptions
# Synchronize only this one file.
# If you remove the file mask, all files in the folder are synchronized:
$transferOptions.FileMask = "install_firefox.exe"
$session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, "$dir\Apps", "/folder",
$False, $False, [WinSCP.SynchronizationCriteria]::Time,
$transferOptions).Check()
To use the assembly, just extract a contents of .NET assembly package to your script folder. No other installation is needed.
The assembly supports not only the MDTM, but also other alternative methods to retrieve the timestamp.
See also a related Powershell example that shows both the above code and other techniques.
(I'm the author of WinSCP)
First off I apologize for the extremely long, wordy post. It’s an interesting issue and I wanted to be as detailed as possible. I’ve tried looking through any related PowerShell posts on the site but I couldn’t find anything that helped me with troubleshooting this problem.
I've been working on a PowerShell script with a team that can send Wake-On-Lan packets to a group of computers. It works by reading a .csv file that has the hostnames and MAC’s in two columns, then it creates the WOL packets for each computer and broadcasts them out on the network. After the WOL packets are sent, it waits a minute and then pings the computers to verify they are online, and if any don’t respond it will display a window with what machines didn’t respond to a ping. Up until the final If/Else statement works fine, so I won't be going into too much detail on that part of the script (but of course if you want/need further details please feel free to ask).
The problem I’m having is with the final If/Else statement. The way the script is supposed to work is that in the ForEach loop in the middle of the script, the value of variable $PingResult is true or false depending on whether or not the computer responds to a ping. If the ping fails, $PingResult is $false, and then it adds the hostname to the $PingResult2 variable.
In theory if all of the machines respond, the If statement fires and the message box displays that it was a success and then the script stops. If any machines failed to respond, the Else statement runs and it joins all of the items together from the $PingResult2 variable and displays the list in a window.
What actually happens is that even if all of the machines respond to a ping, the If statement is completely skipped and the Else statement runs instead. However, at that point the $PingResult2 variable is blank and hence it doesn’t display any computer names of machines that failed to respond. In my testing I’ve never seen a case where the script fails to wake a computer up (assuming it’s plugged in, etc.), but the Else statement still runs regardless. In situations where the Else statement runs, I’ve checked the value of the $PingResult2 variable and confirmed that it is blank, and typing $PingResult2 –eq “” returns $true.
To add another wrinkle to the problem, I want to return to the $PingResult2 variable. I had to create the variable as a generic list so that it would support the Add method to allow the variable to grow as needed. As a test, we modified the script to concatenate the results together by using the += operator instead of making $PingResult2 a list, and while that didn’t give a very readable visual result in the final display window if machines failed, it did actually work properly occasionally. If all of the computers responded successfully the If statement would run as expected and display the success message. Like I said, it would sometimes work and sometimes not, with no other changes making a difference in the results. One other thing that we tried was taking out all of the references to the Visual Basic assembly and other GUI elements (besides the Out-GridView window) and that didn’t work either.
Any idea of what could be causing this problem? Me and my team are completely tapped out of ideas at this point and we’d love to figure out what’s causing the issue. We’ve tried it on Windows 7, 8.1, and the latest preview release of Windows 10 with no success. Thanks in advance for any assistance.
P.S Extra brownie points if you can explain what the regular expression on line 29 is called and how it exactly works. I found out about it on a web posting that resolved the issue of adding a colon between every two characters, but the posting didn’t explain what it was called. (Original link http://powershell.org/wp/forums/topic/add-colon-between-every-2-characters/)
Original WOL Script we built the rest of the script around was by John Savill (link http://windowsitpro.com/networking/q-how-can-i-easily-send-magic-packet-wake-machine-my-subnet)
Script
Add-Type -AssemblyName Microsoft.VisualBasic,System.Windows.Forms
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.ShowDialog() | Out-Null
$FileVerify = Get-Content -Path $OpenFileDialog.FileName -TotalCount 1
$FileVerify = ($FileVerify -split ',')
If($FileVerify[0] -ne "Machine Name" -or $FileVerify[1] -ne "MAC")
{
$MsgBox = [System.Windows.Forms.MessageBox]::Show("The CSV File's headers must be Machine Name and MAC.",'Invalid CSV File headers!',0,48)
Break
}
$ComputerList = Import-Csv -Path $OpenFileDialog.FileName |
Out-GridView -PassThru -Title "Select Computers to Wake up"
ForEach($Computer in $ComputerList)
{
If($Computer.'MAC' -notmatch '([:]|[-])')
{
$Computer.'MAC' = $Computer.'MAC' -replace '(..(?!$))','$1:'
}
$MACAddr = $Computer.'MAC'.split('([:]|[-])') | %{ [byte]('0x' + $_) }
$UDPclient = new-Object System.Net.Sockets.UdpClient
$UDPclient.Connect(([System.Net.IPAddress]::Broadcast),4000)
$packet = [byte[]](,0xFF * 6)
$packet += $MACAddr * 16
[void] $UDPclient.Send($packet, $packet.Length)
write "Wake-On-Lan magic packet sent to $($Computer.'Machine Name'.ToUpper())"
}
Write-Host "Pausing for sixty seconds before verifying connectivity."
Start-Sleep -Seconds 60
$PingResult2 = New-Object System.Collections.Generic.List[System.String]
ForEach($Computer in $ComputerList)
{
Write-Host "Pinging $($Computer.'Machine Name')"
$PingResult = Test-Connection -ComputerName $Computer.'Machine Name' -Quiet
If ($PingResult -eq $false)
{
$PingResult2.Add($Computer.'Machine Name')
}
}
If($PingResult2 -eq "")
{
[System.Windows.Forms.MessageBox]::Show("All machines selected are online.",'Success',0,48)
Break
}
Else
{
$PingResult2 = ($PingResult2 -join ', ')
[System.Windows.Forms.MessageBox]::Show("The following machines did not respond to a ping: $PingResult2",'Unreachable Machines',0,48)
}
The comparison in your If statement is incorrect because you are comparing $PingResult2, a List<string>, to a string. Instead, try
If ($PingResult2.Count -eq 0)
{
# Show the message box
}
Else
{
# Show the other message box
}
or one of countless other variations on this theme.
The regular expression in question uses a backreference to replace exactly two characters with the same two characters plus a colon character. I am unsure what exactly you are attempting to "define," though.
You are checking if a list has a value of a null string, rather than checking the number of items in the list.
If you change the if statement to the following it should work fine:
If($PingResult2.count -eq 0)
I'm guessing the regex is trying to insert a colon between every two characters of a string to represent 0123456789ab as 01:23:45:67:89:ab.
The code means if there is no hyphen or colon in the MAC, put in a colon every the characters, then split the address using colon as delimiter then represent each as a byte:
If($Computer.'MAC' -notmatch '([:]|[-])')
{
$Computer.'MAC' = $Computer.'MAC' -replace '(..(?!$))','$1:'
}
$MACAddr = $Computer.'MAC'.split('([:]|[-])') | %{ [byte]('0x' + $_) }
The other answer have explained quite well why your code does not work. I'm not going there. Instead I'll give some suggestions that I think would improve your script, and explain why I think so. Let's start with functions. Some of the things you do are functions I keep on hand because, well, they work well and are used often enough that I like having them handy.
First, your dialog to get the CSV file path. It works, don't get me wrong, but it could probably be better... As it is you pop up an Open File dialog with no parameters. This function allows you to use a few different parameters as wanted, or none for a very generic Open File dialog, but I think it's a slight improvement here:
Function Get-FilePath{
[CmdletBinding()]
Param(
[String]$Filter = "|*.*",
[String]$InitialDirectory = "C:\")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $InitialDirectory
$OpenFileDialog.filter = $Filter
[void]$OpenFileDialog.ShowDialog()
$OpenFileDialog.filename
}
Then just call it as such:
$CSVFile = Get-FilePath -Filter "Comma Separated Value (.CSV)|*.CSV" -InitialDirectory "$env:USERPROFILE\Desktop"
That opens the dialog filtering for only CSV files, and starts them looking at their desktop (I find that a lot of people save things to their desktop). That only gets the path, so you would run your validation like you were. Actually, not like you were. You really seem to have over complicated that whole bit. Bit I'll get to that in a moment, first, another function! You call message boxes fairly often, and type out a bunch of options, and call the type, and everything every single time. If you're going to do it more than once, make it easy on yourself, make a function. Here, check this out:
Function Show-MsgBox ($Text,$Title="",[Windows.Forms.MessageBoxButtons]$Button = "OK",[Windows.Forms.MessageBoxIcon]$Icon="Information"){
[Windows.Forms.MessageBox]::Show("$Text", "$Title", [Windows.Forms.MessageBoxButtons]::$Button, $Icon) | ?{(!($_ -eq "OK"))}
}
Then you can specify as much or as little as you want for it. Plus it uses Type'd parameters, so tab completion works, or in the ISE (if that's where you're writing your script, like I do) it will pop up valid options and you just pick from a list for the buttons or icon to show. Plus it doesn't return anything if it's a simple 'OK' response, to keep things clean, but will return Yes/No/Cancel or whatever other option you choose for buttons.
Ok, that's the functions, let's get to the meat of the script. Your file validation... Ok, you pull the first line of the file, so that should just be a string, I'm not sure why you're splitting it and verifying each header individually. Just match the string as a whole. I would suggest doing it case insensitive, since we don't really care about case here. Also, depending on how the CSV file was generated, there could be quotes around headers, which you may want to account for. Using -Match will perform a RegEx match that is a bit more forgiving.
If((Get-Content $CSVFile -TotalCount 1) -match '^"?machine name"?,"?mac"?$'){
Show-MsgBox "The CSV File's headers must be Machine Name and MAC." 'Invalid CSV File headers!' -Icon Warning
break
}
So now we have two functions, and 5 lines of code. Yes, the functions take up more space than what you previously had, but they're friendlier to work with, and IMO more functional. Your MAC address correction, and WOL sending part are all aces so far as I'm concerned. There's no reason to change that part. Now, for validating that computers came back up... here we could use some improvement. Instead of making a [List] just add a member to each object, then filter against that below. The script as a whole would be a little longer, but better off for it I think.
Add-Type -AssemblyName Microsoft.VisualBasic,System.Windows.Forms
Function Get-FilePath{
[CmdletBinding()]
Param(
[String]$Filter = "|*.*",
[String]$InitialDirectory = "C:\")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $InitialDirectory
$OpenFileDialog.filter = $Filter
[void]$OpenFileDialog.ShowDialog()
$OpenFileDialog.filename
}
Function Show-MsgBox ($Text,$Title="",[Windows.Forms.MessageBoxButtons]$Button = "OK",[Windows.Forms.MessageBoxIcon]$Icon="Information"){
[Windows.Forms.MessageBox]::Show("$Text", "$Title", [Windows.Forms.MessageBoxButtons]::$Button, $Icon) | ?{(!($_ -eq "OK"))}
}
#Get File Path
$CSVFile = Get-FilePath -Filter "Comma Separated Value (.CSV)|*.CSV" -InitialDirectory "$env:USERPROFILE\Desktop"
#Validate Header
If((Get-Content $CSVFile -TotalCount 1) -match '^"?machine name"?,"?mac"?$'){
Show-MsgBox "The CSV File's headers must be Machine Name and MAC." 'Invalid CSV File headers!' -Icon Warning
break
}
$ComputerList = Import-Csv -Path $CSVFile |
Out-GridView -PassThru -Title "Select Computers to Wake up"
ForEach($Computer in $ComputerList)
{
If($Computer.'MAC' -notmatch '([:]|[-])')
{
$Computer.'MAC' = $Computer.'MAC' -replace '(..(?!$))','$1:'
}
$MACAddr = $Computer.'MAC'.split('([:]|[-])') | %{ [byte]('0x' + $_) }
$UDPclient = new-Object System.Net.Sockets.UdpClient
$UDPclient.Connect(([System.Net.IPAddress]::Broadcast),4000)
$packet = [byte[]](,0xFF * 6)
$packet += $MACAddr * 16
[void] $UDPclient.Send($packet, $packet.Length)
write "Wake-On-Lan magic packet sent to $($Computer.'Machine Name'.ToUpper())"
}
Write-Host "Pausing for sixty seconds before verifying connectivity."
Start-Sleep -Seconds 60
$ComputerList|ForEach
{
Write-Host "Pinging $($_.'Machine Name')"
Add-Member -InputObject $_ -NotePropertyName "PingResult" -NotePropertyValue (Test-Connection -ComputerName $Computer.'Machine Name' -Quiet)
}
If(($ComputerList|Where{!($_.PingResult)}).Count -gt 0)
{
Show-MsgBox "All machines selected are online." 'Success'
}
Else
{
Show-MsgBox "The following machines did not respond to a ping: $(($ComputerList|?{!($_.PingResult)}) -join ", ")" 'Unreachable Machines' -Icon Asterisk
}
Ok, I'm going to get off my soap box and go home, my shift's over and it's time for a cold one.
I am using the following script
http://www.sonassi.com/knowledge-base/magento-kb/mass-update-stock-levels-in-magento-fast/
It works beautifully with the test CSV file.
My POS creates a CSV file but it puts a different heading so the script does not work. I want to automate the process. Is there any way to change the names of headers automatically?
The script requires the headers to be
“sku”,”qty”
my CSV is
“ITEM”,”STOCK”
Is there any way for these two different names to be linked within the script so that my script sees ITEM as sku and STOCK as qty?
You should create a php script with an input of the yourfilename.csv, which is the unformatted file.
$file = file_get_contents('yourfilename.csv');
$file = str_replace('ITEM', 'sku', $file);
$file = str_replace('STOCK', 'qty', $file);
file_put_contents('yourfilename.csv', $file);
The below links are for your reference.
find and replace values in a flat-file using PHP
http://forums.phpfreaks.com/index.php?topic=327900.0
Hope it helps.
Cheers
PHP isn't usually the best way to go for file manipulation granting the fact you have SSH access.
You could also run the following commands (if you have perl installed, which is default in most setups...):
perl -pi -e 's/ITEM/sku/g' /path/to/your/csvfile.csv
perl -pi -e 's/STOCK/qty/g' /path/to/your/csvfile.csv
If you want qty update using raw sql way then you can create a function like below:
function _updateStocks($data){
$connection = _getConnection('core_write');
$sku = $data[0];
$newQty = $data[1];
$productId = _getIdFromSku($sku);
$attributeId = _getAttributeId();
$sql = "UPDATE " . _getTableName('cataloginventory_stock_item') . " csi,
" . _getTableName('cataloginventory_stock_status') . " css
SET
csi.qty = ?,
csi.is_in_stock = ?,
css.qty = ?,
css.stock_status = ?
WHERE
csi.product_id = ?
AND csi.product_id = css.product_id";
$isInStock = $newQty > 0 ? 1 : 0;
$stockStatus = $newQty > 0 ? 1 : 0;
$connection->query($sql, array($newQty, $isInStock, $newQty, $stockStatus, $productId));
}
And call the above function by passing csv row data as arguments. This is just a hint.
In order to get full working code with details you can refer to the following blog article:
Updating product qty in Magento in an easier & faster way
Hope this helps!